CountDownLatch:别浪,等人齐再团!

2025-05-29 0 74

CountDownLatch:别浪,等人齐再团!

一入王者深似海,从此对象是路人。

哈喽观众老爷们你们好,在下战神吕布字奉先,今天给大家来一部吕布的教学视频!

咳咳,不对。大家好,我是磊哥,今天给大家来一篇 CountDownLatch 的文章。

在开始之前,先问大家一个非常专业的技术性问题:打团战最怕_____?

一道非常简单的送分题,如果答不对,那磊哥就要批评你了,哈哈。

可能有人会说:打团战最怕猪队友,但比猪队友更可怕的是打团战人不齐啊兄弟,想想在打团时如果是 5V2 是怎么一幅画面,心痛到不敢想。

等人齐再团

磊哥在儿子没有出生之前,也是资深的农药玩家,至于段位吗?别问!问就是青铜。虽然磊哥的段位不是很高,但基本的大局观还是有的,毕竟也是打过几年 Dota 和 LOL 的青铜玩家是吧?哈哈。

农药和其他 Moba 类游戏是一样的,想要取胜,必须要把握好每次团战,而每次团战的关键在于等人齐了再开团,是吧?而这个思想正好和咱们要讲得 CountDownLatch 的思想是一致的,咱们来看看是怎么回事吧。

吾有上将“CountDownLatch”

想象一下这样一个场景,当我们需要等待某些线程执行完之后,再执行主线程的代码,要怎么实现?

可能有人会说,简单,用 join() 方法等待线程执行完成之后再执行主线程就行了,实现代码是这样的:

  1. //创建线程1
  2. Threadt1=newThread(newRunnable(){
  3. @Override
  4. publicvoidrun(){
  5. //dosomething
  6. }
  7. });
  8. t1.start();
  9. //创建线程2
  10. Threadt2=newThread(newRunnable(){
  11. @Override
  12. publicvoidrun(){
  13. //dosomething
  14. }
  15. });
  16. t2.start();
  17. //等待线程1和线程2执行完
  18. t1.join();
  19. t2.join();

当然,如果使用的是 Thread 来执行任务,那这种写法也是可行的。然而真实的(编码)环境中我们是不会使用 Thread 来执行多任务的,而是会使用线程池来执行多任务,这样可以避免线程重复启动和销毁所带来的性能开销,实现代码如下:

  1. //创建固定线程数的线程池
  2. ExecutorServiceexecutorService=Executors.newFixedThreadPool(2);
  3. //任务一
  4. executorService.submit(newRunnable(){
  5. @Override
  6. publicvoidrun(){
  7. //dosomething
  8. }
  9. });
  10. //任务二
  11. executorService.submit(newRunnable(){
  12. @Override
  13. publicvoidrun(){
  14. //dosomething
  15. }
  16. });

那么这时候问题来了线程池是没有 join() 方法的,那要怎么实现等待呢?

这个时候就要派出我方大将“CountDownLatch”啦。

吾有上将潘凤,可斩华雄… 出场数秒,潘凤…“卒”。

等等导演,我觉得剧情应该是这样的…

CountDownLatch使用

为了实现等待所有线程池执行完之后再执行主线程的逻辑,我决定使用 AQS(AbstractQueuedSynchronizer,抽象同步框架)下的著名类 CountDownLatch 来实现此功能,具体的实现代码如下:

  1. publicstaticvoidmain(String[]args)throwsInterruptedException{
  2. //创建CountDownLatch
  3. CountDownLatchcountDownLatch=newCountDownLatch(2);
  4. //创建固定线程数的线程池
  5. ExecutorServiceexecutorService=Executors.newFixedThreadPool(2);
  6. //任务一
  7. executorService.submit(newRunnable(){
  8. @Override
  9. publicvoidrun(){
  10. //dosomething
  11. try{
  12. //让此任务执行1.2s
  13. Thread.sleep(1200);
  14. }catch(InterruptedExceptione){
  15. e.printStackTrace();
  16. }
  17. System.out.println("我是任务一");
  18. countDownLatch.countDown();
  19. }
  20. });
  21. //任务二
  22. executorService.submit(newRunnable(){
  23. @Override
  24. publicvoidrun(){
  25. //dosomething
  26. try{
  27. //让此任务执行1.2s
  28. Thread.sleep(1000);
  29. }catch(InterruptedExceptione){
  30. e.printStackTrace();
  31. }
  32. System.out.println("我是任务二");
  33. countDownLatch.countDown();
  34. }
  35. });
  36. //等待任务执行完成
  37. countDownLatch.await();
  38. System.out.println("程序执行完成~");
  39. }

以上程序执行结果如下:

CountDownLatch:别浪,等人齐再团!

从上述结果可以看出,主线程的执行是等待任务一和任务二都执行完成之后才执行的。

CountDownLatch实现原理

CountDownLatch 中 count down 是倒数的意思,latch 则是门闩的含义。整体含义可以理解为倒数的门栓,似乎有点“321,芝麻开门”的感觉,CountDownLatch 的作用也正是如此。

CountDownLatch 在创建的时候需要传入一个整数,在这个整数“倒数”到 0 之前,主线程需要一直挂起等待,直到其他的线程都执行之后,主线才能继续执行。

CountDownLatch执行流程

CountDownLatch 的实现是在其内部创建并维护了一个 volatile 类型的整数计数器,当调用 countDown() 方法时,会尝试将整数计数器 -1,当调用 wait() 方法时,当前线程就会判断整数计数器是否为 0,如果为 0,则继续往下执行,如果不为 0,则使当前线程进入等待状态,直到某个线程将计数器设置为 0,才会唤醒在 await() 方法中等待的线程继续执行。

CountDownLatch常用方法

  1. //线程被挂起直到count值为0才继续执行
  2. publicvoidawait()throwsInterruptedException{};
  3. //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
  4. publicbooleanawait(longtimeout,TimeUnitunit)throwsInterruptedException{};
  5. //将count值减1
  6. publicvoidcountDown(){};

总结

使用 CountDownLatch 可以实现等待所有任务执行完成之后再执行主任务的功能,它就好像比赛中要等待所有运动员都完成比赛之后再公布排名一样,当然我们在玩农药的时候也是一样,要等所有人集合完毕之后再开团,这是制胜的关键。而 CountDownLatch 是通过计数器来实现等待功能的,当创建 CountDownLatch 时会设置一个大于 0 的计数器,每次调用 countDown() 方法时计数器的值会 -1,直到计数器值变为 0 时,等待的任务就可以继续执行了。

参考 & 鸣谢

www.jianshu.com/p/128476015902

原文地址:https://mp.weixin.qq.com/s/72XXWIMJ230bwfu4PUuS-Q

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 CountDownLatch:别浪,等人齐再团! https://www.kuaiidc.com/112666.html

相关文章

发表评论
暂无评论