认真阅读完这篇文章熟练掌握多线程常见锁的基本用法

简介: 认真阅读完这篇文章熟练掌握多线程常见锁的基本用法

CountDownLatch

官方解释:一种同步辅助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成countDownLatch用给定的计数进行初始化。由于对countDown方法的调用,await方法会阻塞直到当前计数达到0,之后所有等待的线程都会被释放,所有后续的await调用都会立即返回。这是一个一次性现象——计数不能重置。如果需要重置计数的版本,可以考虑使用CyclicBarrier。countDownLatch是一个通用的同步工具,可以用于多种目的。用一个计数初始化的CountDownLatch作为一个简单的开/关闩锁,或gate:所有调用await的线程在gate处等待,直到它被调用countDown的线程打开。可以使用初始化为N的CountDownLatch使一个线程等待,直到Nthreads完成某些操作,或者某些操作已经完成N次。CountDownLatch的一个有用属性是,它不要求调用countDown的线程在继续之前等待计数达到0,它只是阻止任何线程继续等待,直到所有线程都可以通过。
案例一、

package duoxiancheng2;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @author yeqv
 * @program A2
 * @Classname T2
 * @Date 2022/2/8 8:38
 * @Email w16638771062@163.com
 */
public class T2 {
    //CountDownLatch
    CountDownLatch latch = new CountDownLatch(1);

    public static void main(String[] args) {

        T2 t = new T2();
        new Thread(() -> t.a(), "A").start();
        new Thread(() -> t.b(), "B").start();
    }

    void a() {
        System.out.println(Thread.currentThread().getName() + "已启动");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        latch.countDown();
        System.out.println(Thread.currentThread().getName() + "已结束");


    }

    void b() {
        System.out.println(Thread.currentThread().getName() + "已启动");
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "已结束");

    }
}

结果:线程A,B启动。线程A在执行,线程B在等待
在这里插入图片描述
当线程A完成操作开锁之后,线程B开始执行(这里是某组操作完成时,也就是执行了countDown()方法。不是说线程A执行完。可能执行到中间就已经开锁了)
在这里插入图片描述

ReentrantLock

ReentrantLock 最大的特点公平锁
即哪个线程等待的时间最长优先执行
ReentrantLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。
(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。

(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。

public class T3 {
    Lock lock = new ReentrantLock(true);//设置为true公平锁,效率低但公平

    void m() {
        for (int i = 0; i <= 20; i++) {
            lock.lock();
            System.out.println(Thread.currentThread().getName());
            try{
                TimeUnit.SECONDS.sleep(1);
            }catch(Exception e){
                e.printStackTrace();
            }
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        var t = new T3();
        new Thread(t::m,"T1").start();
        new Thread(t::m,"T2").start();
        new Thread(t::m,"T3").start();
        new Thread(t::m,"T4").start();
    }
}

ReadWriteLock

ReadWriteLock也是一个接口,提供了readLock和writeLock两种锁的操作机制,一个资源可以被多个线程同时读,或者被一个线程写,但是不能同时存在读和写线程。
假设在程序中定义一个共享的数据结构用作缓存,它大部分时间提供读服务(例如:查询和搜索),而写操作占有的时间很少,但是写操作完成之后的更新需要对后续的读服务可见。
特性:
读-读不互斥
读-写互斥
写-写互斥
案例、

public class TestReadWriteLock{
    public static void main(String[] args){
        ReadWriteLockDemo rw = new ReadWriteLockDemo();

        // 一个线程进行写
        new Thread(new Runnable(){
            public void run(){
                rw.set((int)(Math.random()*100));
            }
        },"Write:").start();


        // 100个线程进行读操作
        for(int i=0; i<100; i++){
            new Thread(new Runnable(){
                public void run(){
                rw.get();
                }
            },"Read:").start();
        }
    }
}
 
class ReadWriteLockDemo{
    private int number = 0;
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    // 读
    public void get(){
        lock.readLock().lock(); // 上锁
        try{
            System.out.println(Thread.currentThread().getName()+":"+number);
        }finally{
            lock.readLock().unlock(); // 释放锁
        }
    }
    // 写
    public void set(int number){
        lock.writeLock().lock();
        try{
            System.out.println(Thread.currentThread().getName());
            this.number = number;
        }finally{
            lock.writeLock().unlock();
        }
    }
}
相关文章
|
2月前
|
安全 Java 编译器
线程安全问题和锁
本文详细介绍了线程的状态及其转换,包括新建、就绪、等待、超时等待、阻塞和终止状态,并通过示例说明了各状态的特点。接着,文章深入探讨了线程安全问题,分析了多线程环境下变量修改引发的数据异常,并通过使用 `synchronized` 关键字和 `volatile` 解决内存可见性问题。最后,文章讲解了锁的概念,包括同步代码块、同步方法以及 `Lock` 接口,并讨论了死锁现象及其产生的原因与解决方案。
81 10
线程安全问题和锁
|
2月前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
44 2
|
2月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
1月前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
33 1
|
1月前
|
Java 应用服务中间件 测试技术
Java21虚拟线程:我的锁去哪儿了?
【10月更文挑战第8天】
32 0
|
1月前
|
安全 调度 数据安全/隐私保护
iOS线程锁
iOS线程锁
26 0
|
1月前
|
Java API
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
31 0
|
1月前
|
安全 Java 程序员
【多线程-从零开始-肆】线程安全、加锁和死锁
【多线程-从零开始-肆】线程安全、加锁和死锁
43 0
|
1月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
2月前
|
存储 算法 Java
关于python3的一些理解(装饰器、垃圾回收、进程线程协程、全局解释器锁等)
该文章深入探讨了Python3中的多个重要概念,包括装饰器的工作原理、垃圾回收机制、进程与线程的区别及全局解释器锁(GIL)的影响等,并提供了详细的解释与示例代码。
28 0