你真的懂CountDownLatch (倒计时器)吗?

前言:

CountDownLatch允许 count 个线程阻塞在一个地方,直至所有线程的任务都执行完毕。在 Java 并发中,countdownlatch 的概念是一个常见的面试题,所以一定要确保你很好的理解了它。CountDownLatch是共享锁的一种实现,它默认构造 AQS 的 state 值为 count。当线程使用countDown方法时,其实使用了 tryReleaseShared 方法以CAS的操作来减少state,直至state为0就代表所有的线程都调用了countDown方法。当调用await方法的时候,如果state不为0,就代表仍然有线程没有调用countDown方法,那么就把已经调用过countDown的线程都放入阻塞队列Park,并自旋CAS判断state== 0,直至最后一个线程调用了countDown,使得state == 0,于是阻塞的线程便判断成功,全部往下执行。

1 CountDownLatch 的两种典型用法

(1)某一线程在开始运行前等待 n 个线程执行完毕。将 CountDownLatch 的计数器初始化为 n : newCountDownLatch(n) ,每当一个任务线程执行完毕,就将计数器减 1countdownlatch.countDown() ,当计数器的值变为 0 时,在 CountDownLatch上 await() 的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。

(2) 实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的 CountDownLatch 对象,将其计数器初始化为 1 : new CountDownLatch(1) ,多个线程在开始执行任务前首先 coundownlatch.await() ,当主线程调用 countDown() 时,计数器变为 0,多个线程同时被唤醒。

2 CountDownLatch 的使用示例

/**

*

* @author SnailClimb

* @date 2018年10月1日

* @Description: CountDownLatch 使用方法示例

*/

public class CountDownLatchExample1 {

 // 请求的数量

 private static final int threadCount = 550;

 public static void main(String[] args) throws InterruptedException {

  // 创建一个具有固定线程数量的线程池对象(如果这里线程池的线程数量给太少的话你会发现执行的很

慢)

  ExecutorService threadPool = Executors.newFixedThreadPool(300);

  final CountDownLatch countDownLatch = new CountDownLatch(threadCount);

  for (int i = 0; i < threadCount; i++) {

   final int threadnum = i;

   threadPool.execute(() -> {// Lambda 表达式的运用

    try {

     test(threadnum);

   } catch (InterruptedException e) {

     // TODO Auto-generated catch block

     e.printStackTrace();

   } finally {

     countDownLatch.countDown();// 表示一个请求已经被完成

   }

});

 }

  countDownLatch.await();

  threadPool.shutdown();

  System.out.println("finish");

}

 public static void test(int threadnum) throws InterruptedException {

  Thread.sleep(1000);// 模拟请求的耗时操作

  System.out.println("threadnum:" + threadnum);

  Thread.sleep(1000);// 模拟请求的耗时操作

}

上面的代码中,我们定义了请求的数量为 550,当这 550 个请求被处理完成之后,才会执行System.out.println(“finish”); 。

与 CountDownLatch 的第一次交互是主线程等待其他线程。主线程必须在启动其他线程后立即调用CountDownLatch.await() 方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。

其他 N 个线程必须引用闭锁对象,因为他们需要通知 CountDownLatch 对象,他们已经完成了各自的任务。这种通知机制是通过 CountDownLatch.countDown() 方法来完成的;每调用一次这个方法,在构造函数中初始化的 count 值就减 1。所以当 N 个线程都调 用了这个方法,count 的值等于 0,然后主线程就能通过 await() 方法,恢复执行自己的任务。

注意: CountDownLatch 的 await() 方法使用不当很容易产生死锁,比如我们上面代码中的 for循环改为:

for (int i = 0; i < threadCount-1; i++) {

.......

}

3 CountDownLatch 的不足

CountDownLatch 是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当 CountDownLatch 使用完毕后,它不能再次被使用。

**

以上就是本期更新内容,关注下期更精彩哦!

**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值