文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

闭锁和栅栏的区分以及适用场景

2024-12-11 21:21

关注

相信小伙伴对这个两个词或多或少都有些了解,他们是在并发编程中常用的线程通讯工具。两者十分相似,但是又有不同,导致很多小伙伴也包括我在内产生了很多困惑:他们两个究竟有什么区别,以及适用于什么场景呢?

[[330337]]

下面听我缓缓道来,不想看例子或者过程的小伙伴可以拉到最下面看总结呦

闭锁

闭锁(CountDownLatch)坊间俗称计数器,官方(谷歌机翻,哈哈)解释:

 

  1.  
  2.  
  3. 允许一个或多个线程等待,直到在其他线程中执行的一组操作完成的同步辅助程序。 

大概意思就是说,可以有一个或者多个线程,等待其他线程都完成某个操作后,再继续执行。

什么意思呢?举个栗子吧:

生活中应该经常遇见一种情况,坐公交车是,尤其是始发站,司机师傅往往为了一次拉更多的乘客,会等到车上乘客的数量到达一定程度以后才会发车。测试代码如下:

 

  1. public static void main(String[] args) { 
  2.         List list = new ArrayList<>(); 
  3.         Passenger p1 = new Passenger("看会书"); 
  4.         Passenger p2 = new Passenger("看会手机"); 
  5.         Passenger p3 = new Passenger("看会风景"); 
  6.         Passenger p4 = new Passenger("看会售票员"); 
  7.         list.add(p1); 
  8.         list.add(p2); 
  9.         list.add(p3); 
  10.         list.add(p4); 
  11.         ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 200, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), new ThreadFactory() { 
  12.             private ThreadGroup group = (null == System.getSecurityManager() ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup()); 
  13.             private AtomicInteger num = new AtomicInteger(); 
  14.             @Override 
  15.             public Thread newThread(Runnable r) { 
  16.                 Thread thread = new Thread(group, r,"zoo" + num.getAndIncrement(),0); 
  17.                 thread.setDaemon(false); 
  18.                 return thread; 
  19.             } 
  20.         }, new ThreadPoolExecutor.CallerRunsPolicy()); 
  21.         //设定闭锁释放阈值 
  22.         CountDownLatch countDownLatch = new CountDownLatch(list.size()); 
  23.         log.error("司机师傅人够一车再发车,等会人吧..."); 
  24.         for (Passenger p : list) { 
  25.             executor.execute(()->gotoZOO(p,countDownLatch)); 
  26.         } 
  27.         try { 
  28.             countDownLatch.await(); 
  29.             log.error("人够了,起飞!"); 
  30.             executor.shutdown(); 
  31.         } catch (InterruptedException e) { 
  32.             e.printStackTrace(); 
  33.         } 
  34.  
  35.     } 
  36.  
  37.     private static void  gotoZOO(Passenger p,CountDownLatch countDownLatch){ 
  38.         log.error("{}的乘客上车啦",p.getDoWhat()); 
  39.         try { 
  40.             countDownLatch.countDown(); 
  41.             log.error("{}",p.doWhatOnBus()); 
  42.         } catch (Exception e) { 
  43.             e.printStackTrace(); 
  44.         } 
  45.     } 
  46.  
  47.     static class Passenger{ 
  48.         private String doWhat; 
  49.  
  50.         public Passenger(String doWhat) { 
  51.             this.doWhat = doWhat; 
  52.         } 
  53.  
  54.         public String getDoWhat() { 
  55.             return doWhat; 
  56.         } 
  57.  
  58.         public String doWhatOnBus() { 
  59.             return "车上好无聊啊,"+doWhat+"吧!"
  60.         } 
  61.     } 

执行结果

 

  1. 23:46:34.698 [main] ERROR com.test - 司机师傅人够一车再发车,等会人吧... 
  2. 23:46:34.757 [zoo1] ERROR com.test - 看会手机的乘客上车啦 
  3. 23:46:34.758 [zoo3] ERROR com.test - 看会售票员的乘客上车啦 
  4. 23:46:34.757 [zoo0] ERROR com.test - 看会书的乘客上车啦 
  5. 23:46:34.759 [zoo1] ERROR com.test - 车上好无聊啊,看会手机吧! 
  6. 23:46:34.759 [zoo3] ERROR com.test - 车上好无聊啊,看会售票员吧! 
  7. 23:46:34.757 [zoo2] ERROR com.test - 看会风景的乘客上车啦 
  8. 23:46:34.759 [zoo0] ERROR com.test - 车上好无聊啊,看会书吧! 
  9. 23:46:34.759 [zoo2] ERROR com.test - 车上好无聊啊,看会风景吧! 
  10. 23:46:34.759 [main] ERROR com.test - 人够了,起飞! 

司机师傅(主线程)要等上了4个乘客以后才发车(等待4个子线程完成完成某件事以后调用countDown方法),而乘客上车(调用countDown)以后该做自己的事还做自己的事情,不会因为上了车就傻呆呆的什么都不干了(不会因为调用了countDown而阻塞自身)。等司机师傅看人够了(到达设定阈值),就发车了。

闭锁总结:

栅栏

栅栏(CyclicBarrier)官方解释:

 

  1.  
  2. 同步帮助,允许一组线程互相等待,以达到共同的障碍点。 CyclicBarriers在涉及固定大小的线程方的程序中很有用,这些线程有时必须互相等待。该屏障称为 cyclic ,因为它可以在释放等待线程后重新使用。 

从类注释上我们可以大致了解到,他是运用在一组,也即是多个线程中的,当所有线程到达某个状态前一直阻塞,直到所有线程都达到后再继续执行。而且是可以重复使用的。

上面的描述还是太晦涩了,还是举个栗子:

我们小时候学校都组织过春游,规定好地点,等人到齐了就一起进去玩。写了个简单的例子,看这种场景栅栏是怎么工作的

 

  1. public static void main(String[] args) { 
  2.         List list = new ArrayList<>(); 
  3.         Boy boy1 = new Boy("看老虎"); 
  4.         Boy boy2 = new Boy("看猩猩"); 
  5.         Boy boy3 = new Boy("看狮子"); 
  6.         Boy boy4 = new Boy("看售票员"); 
  7.         list.add(boy1); 
  8.         list.add(boy2); 
  9.         list.add(boy3); 
  10.         list.add(boy4); 
  11.         ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 200, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), new ThreadFactory() { 
  12.             private ThreadGroup group = (null == System.getSecurityManager() ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup()); 
  13.             private AtomicInteger num = new AtomicInteger(); 
  14.             @Override 
  15.             public Thread newThread(Runnable r) { 
  16.                 Thread thread = new Thread(group, r,"zoo" + num.getAndIncrement(),0); 
  17.                 thread.setDaemon(false); 
  18.                 return thread; 
  19.             } 
  20.         }, new ThreadPoolExecutor.CallerRunsPolicy()); 
  21.         //初始化栅栏,设置障碍点阈值 
  22.         CyclicBarrier cyclicBarrier = new CyclicBarrier(list.size()); 
  23.         for (Boy boy : list) { 
  24.             executor.execute(()->gotoZOO(boy,cyclicBarrier)); 
  25.         } 
  26.     } 
  27.  
  28.     private static void  gotoZOO(Boy boy,CyclicBarrier cyclicBarrier){ 
  29.         log.error("人还没到齐呢,等一下吧,{}的小男孩开始等待",boy.getWhere()); 
  30.         try { 
  31.             cyclicBarrier.await(); 
  32.             log.error("{}",boy.goWhere()); 
  33.         } catch (Exception e) { 
  34.             e.printStackTrace(); 
  35.         } 
  36.     } 
  37.  
  38.     static class Boy{ 
  39.         private String where
  40.         public Boy(String where) { 
  41.             this.where = where
  42.         } 
  43.  
  44.         public String getWhere() { 
  45.             return where
  46.         } 
  47.  
  48.         public String goWhere() { 
  49.             return "人到齐了,我要去"+where+"啦!"
  50.         } 
  51.     } 

执行结果:

 

  1. 22:05:59.476 [zoo2] ERROR com.test - 人还没到齐呢,等一下吧,看狮子的小男孩开始等待 
  2. 22:05:59.477 [zoo1] ERROR com.test - 人还没到齐呢,等一下吧,看猩猩的小男孩开始等待 
  3. 22:05:59.477 [zoo0] ERROR com.test - 人还没到齐呢,等一下吧,看老虎的小男孩开始等待 
  4. 22:05:59.476 [zoo3] ERROR com.test - 人还没到齐呢,等一下吧,看售票员的小男孩开始等待 
  5. 22:05:59.484 [zoo0] ERROR com.test - 人到齐了,我要去看老虎啦! 
  6. 22:05:59.484 [zoo2] ERROR com.test - 人到齐了,我要去看狮子啦! 
  7. 22:05:59.484 [zoo3] ERROR com.test - 人到齐了,我要去看售票员啦! 
  8. 22:05:59.484 [zoo1] ERROR com.test - 人到齐了,我要去看猩猩啦! 

我们可以发现前三个小男孩在到达以后都没有进到动物园里,而是直到第四个小男孩来到以后,四个小男孩才进入动物园,在此之前每来一个小朋友就多一个小朋友等待(每个线程调用await方法),直到等待所有人到齐(线程阻塞等待达到栅栏障碍点4),各个小男孩再去继续进入动物园看动物(各线程继续执行自己的任务)。就像是动物园大门的栅栏,买的是团体票,每次必须人到齐才放开让小朋友进去一样。

栅栏总结

各子线程相互等待,直到达到栅栏初始化时的阈值,则继续执行

区分以及个人理解

闭锁:有点类似于一个统计功能(可能这也是为什么他俗称计数器),主线程调用await方法阻塞等待统计结果,而子线程只负责在达到统计要求时调用countDown方法告诉主线程我好了,而不会阻塞本身;有一个负责接收结果(主线程)和一个或多个发送数量的(子线程);

栅栏:首先在线程调用await方法时会阻塞当前线程,其次个人理解他没有类似像闭锁那样的主子的关系,他是各个线程相互等待,都到达某个点的时候,则继续执行。

适用场景

其实从上面的区分就能看出一些:如果是需要将多线程执行完成与否的接口汇总到某一个线程中,然后再继续执行的情况,比如每条线程计算一个指标,都计算完成以后再计算所有指标的总和或者其他的,就可以使用闭锁;

而如果只是各个线程需要等各个线程都完成了,再继续自己的事,可以使用栅栏,比如ABC三个线程分别去获取123三个指标,然后再A要取这三个数的平均数,B要取总和,C要取方差,那就需要等ABC都先取完了123这三个指标,才能计算,这时候就可以用到栅栏了。

总结

这两种都是非常好的线程通讯工具,不过细节还是有所差异。

总得来说就是:

  • 闭锁是为了在某一条线程等待获取到其他线程的执行结果;
  • 而栅栏则是线程间的相互等待,然后再同时开始做各自的事情

最后

文中的代码只是为了比较好的说明两种工具的差异,写的不好还请小伙伴们多多包涵,如果发现有哪点写的不对的也欢迎大家伙们留言,我们共同进步!最后如果小伙伴觉得文章不错,不妨动动小手点个赞再走,不要下次一定呦~

来源:segmentfault.com内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯