CountDownLatch和CyclicBarrier 举例详解
有时候会有这样的需求,多个线程同时工作,然后其中几个可以随意并发执行,但有一个线程需要等其他线程工作结束后,才能开始。举个例子,开启多个线程分块下载一个大文件,每个线程只下载固定的一截,最后由另外一个线程来拼接所有的分段,那么这时候我们可以考虑使用CountDownLatch来控制并发。
CountDownLatch是JAVA提供在java.util.concurrent包下的一个辅助类,可以把它看成是一个计数器,其内部维护着一个count计数,只不过对这个计数器的操作都是原子操作,同时只能有一个线程去操作这个计数器,CountDownLatch通过构造函数传入一个初始计数值,调用者可以通过调用CounDownLatch对象的cutDown()方法,来使计数减1;如果调用对象上的await()方法,那么调用者就会一直阻塞在这里,直到别人通过cutDown方法,将计数减到0,才可以继续执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /** * Created by DELL on 2017/1/4. */ import java.util.concurrent.CountDownLatch; public class Thread2 { /** * 计数器,用来控制线程 * 传入参数2,表示计数器计数为2 */ private final static CountDownLatch mCountDownLatch = new CountDownLatch( 2 ); /** * 示例工作线程类 */ private static class WorkingThread extends Thread { private final String mThreadName; private final int mSleepTime; public WorkingThread(String name, int sleepTime) { mThreadName = name; mSleepTime = sleepTime; } @Override public void run() { System.out.println( "[" + mThreadName + "] started!" ); try { Thread.sleep(mSleepTime); } catch (InterruptedException e) { e.printStackTrace(); } mCountDownLatch.countDown(); //减一 System.out.println( "[" + mThreadName + "] end!" ); } } /** * 示例线程类 */ private static class SampleThread extends Thread { @Override public void run() { System.out.println( "[SampleThread] started!" ); try { // 会阻塞在这里等待 mCountDownLatch 里的count变为0; // 也就是等待另外的WorkingThread调用countDown() mCountDownLatch.await(); } catch (InterruptedException e) { } System.out.println( "[SampleThread] end!" ); } } public static void main(String[] args) throws Exception { // 最先run SampleThread new SampleThread().start(); // 运行两个工作线程 // 工作线程1运行5秒 new WorkingThread( "WorkingThread1" , 5000 ).start(); // 工作线程2运行2秒 new WorkingThread( "WorkingThread2" , 2000 ).start(); } } |
执行结果如下:
1 2 3 4 5 6 7 8 9 10 11 | [SampleThread] started! [WorkingThread2] started! [WorkingThread1] started! [WorkingThread2] end! [WorkingThread1] end! [SampleThread] end! |
CyclicBarrier 例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | import java.util.concurrent.CyclicBarrier; /** * Created by DELL on 2017/1/4. */ public class thread3 { public static final int INIT_NUM = 5 ; public static void main(String[] args) { CyclicBarrier cyc = new CyclicBarrier(INIT_NUM, new Runnable() { @Override public void run() { System.out.println( "init cyclicBarrier----" ); } }); for ( int i = 0 ; i < INIT_NUM; i++) { new sampleCyclic(cyc).start(); } } private static class sampleCyclic extends Thread { CyclicBarrier barrier; public sampleCyclic(CyclicBarrier barrier) { this .barrier = barrier; } @Override public void run() { System.out.println( "start=====" ); try { barrier.await(); } catch (Exception e) { e.printStackTrace(); } System.out.println( "id" + Thread.currentThread().getId() + "working----" ); } } } |
输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 | start===== start===== start===== start===== start===== init cyclicBarrier---- id16working---- id13working---- id15working---- id14working---- id12working---- |
综上所述二者有甚区别呢?
CountDownLatch | CyclicBarrier |
减计数方式 | 加计数方式 |
计算为0时释放所有等待的线程 | 计数达到指定值时释放所有等待线程 |
计数为0时,无法重置 | 计数达到指定值时,计数置为0重新开始 |
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响 | 调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞 |
不可重复利用 | 可重复利用 |
CountDownLatch : 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。CyclicBarrier : N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。
这样应该就清楚一点了,对于CountDownLatch来说,重点是那个“一个线程”, 是它在等待, 而另外那N的线程在把“某个事情”做完之后可以继续等待,可以终止。而对于CyclicBarrier来说,重点是那N个线程,他们之间任何一个没有完成,所有的线程都必须等待。
CountDownLatch 是计数器, 线程完成一个就记一个, 就像 报数一样, 只不过是递减的.
而CyclicBarrier更像一个水闸, 线程执行就想水流, 在水闸处都会堵住, 等到水满(线程到齐)了, 才开始泄流.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了