CountDownLatch 原理

CountDownLatch 原理

CountDownLatch是同步工具类之一,可以指定一个计数值,在并发环境下由线程进行减1操作,当计数值变为0之后,被await方法阻塞的线程将会唤醒,实现线程间的同步。

public void startTestCountDownLatch() {
   //定义一个数值为10的对象
   final CountDownLatch countDownLatch = new CountDownLatch(10);

   for (int i = 0; i < threadNum; i++) {
       final int finalI = i + 1;
       new Thread(() -> {
           System.out.println("thread " + finalI + " start");
           Random random = new Random();
           try {
               Thread.sleep(random.nextInt(10000) + 1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("thread " + finalI + " finish");
			//计数器会减一
			// 这个方法的作用是唤醒AQS的同步队列中,正在等待的第一个线程
        	// 而我们分析acquireSharedInterruptibly方法时已经说过,
        	// 若一个线程被唤醒,检测到count == 0,会继续唤醒下一个等待的线程
        	// 也就是说,这个方法的作用是,在count == 0时,唤醒所有等待的线程
           countDownLatch.countDown();
       }).start();
   }

   try {
   	   //一直等待计数器减到0,程序继续向下执行
   	   //方法的作用是尝试获取共享锁(count==0),若获取失败,则线程将会被加入到AQS的同步队列中等待
       countDownLatch.await();
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
   System.out.println(threadNum + " thread finish");
}

主线程启动10个子线程后阻塞在await方法,需要等所有子线程都执行完毕,主线程才能唤醒继续执行。

CountDownLatch内部定义了一个内部类Sync,继承自AQS,通过这个内部类来实现线程阻塞

private static final class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 4982264981922014374L;
 
    /** 构造方法,接收count值,只有count减小为0时,线程才不会被await方法阻塞 */
    Sync(int count) {
        // CountDownLatch利用AQS的方式就是直接让count作为AQS的同步变量state
        // 所以直接用state记录count值
        setState(count);
    }
 
    /** 获取当前的count值 */
    int getCount() {
        return getState();
    }
 
    /** 
     * 这是AQS的模板方法acquireShared、acquireSharedInterruptibly等方法内部将会调用的方法,
     * 由子类实现,这个方法的作用是尝试获取一次共享锁,对于AQS来说,
     * 此方法返回值大于等于0,表示获取共享锁成功,反之则获取共享锁失败,
     * 而在这里,实际上就是判断count是否等于0,线程能否向下运行
     */
    protected int tryAcquireShared(int acquires) {
        // 此处判断state的值是否为0,也就是判断count是否为0,
        // 若count为0,返回1,表示获取锁成功,此时线程将不会阻塞,正常运行
        // 若count不为0,则返回-1,表示获取锁失败,线程将会被阻塞
        // 从这里我们已经可以看出CountDownLatch的实现方式了
        return (getState() == 0) ? 1 : -1;
    }
 
    /**
     * 此方法的作用是用来是否AQS的共享锁,返回true表示释放成功,反之则失败
     * 此方法将会在AQS的模板方法releaseShared中被调用,
     * 在CountDownLatch中,这个方法用来减小count值
     */
    protected boolean tryReleaseShared(int releases) {
        // 使用死循环不断尝试释放锁
        for (;;) {
            // 首先获取当前state的值,也就是count值
            int c = getState();
            // 若count值已经等于0,则不能继续减小了,于是直接返回false
            // 为什么返回的是false,因为等于0表示之前等待的那些线程已经被唤醒了,
            // 若返回true,AQS会尝试唤醒线程,若返回false,则直接结束,所以
            // 在没有线程等待的情况下,返回false直接结束是正确的
            if (c == 0)
                return false;
            // 若count不等于0,则将其-1
            int nextc = c-1;
            // compareAndSetState的作用是将count值从c,修改为新的nextc
            // 此方法基于CAS实现,保证了操作的原子性
            if (compareAndSetState(c, nextc))
                // 若nextc == 0,则返回的是true,表示已经没有锁了,线程可以运行了,
                // 若nextc > 0,则表示线程还需要继续阻塞,此处将返回false
                return nextc == 0;
        }
    }
}

内部类Sync的实现非常简单,它只实现了AQS中的两个方法,即tryAcquireShared以及tryReleaseShared,这两个方法是AQS提供的使用共享锁的接口。
CountDownLatch实际上是一种共享锁机制,即锁可以同时被多个线程获取,这个不难理解,因为一旦count被减小为0,则所有线程通过await方法时,都能够顺利通过,不会因为获取不到锁而阻塞。而且从上面的实现中我们可以看到,Sync直接将count值作为AQS的state的值,只有state的值为0,线程才能获取锁,也就是获得执行权限。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值