闭锁CountDownLatch和栅栏CyclicBarrier之异同举例
程序员文章站
2022-06-12 14:30:17
...
CountDownLatch和CyclicBarrier的主要联系和区别如下:
1.闭锁CountDownLatch做减计数,而栅栏CyclicBarrier则是加计数。
2.CountDownLatch是一次性的,CyclicBarrier可以重用。
3.CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成。
4.鉴于上面的描述,CyclicBarrier在一些场景中可以替代CountDownLatch实现类似的功能。
另外,值得一提的是,CountDownLatch和CyclicBarrier在创建和启动线程时,都没有明确提到同时启动全部线程,事实上这在技术上是不大可能,不必要,不提倡的。
先看例子一:
这是根据jdk文档中的伪代码例程,编写的一个例子,我们完全可以将这个例程改为只使用一个CountDownLatch来实现之。经过测试,发现begin的引入对程序基本无用,当list是1000的数量级时,最先启动的线程仍然比最后启动的快几十毫秒左右;而不设置begin开始闭锁的程序,也是完全一样的情况。
例子二:
使用栅栏CyclicBarrier实现和上面闭锁CountDownLatch相同的功能。
1.闭锁CountDownLatch做减计数,而栅栏CyclicBarrier则是加计数。
2.CountDownLatch是一次性的,CyclicBarrier可以重用。
3.CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成。
4.鉴于上面的描述,CyclicBarrier在一些场景中可以替代CountDownLatch实现类似的功能。
另外,值得一提的是,CountDownLatch和CyclicBarrier在创建和启动线程时,都没有明确提到同时启动全部线程,事实上这在技术上是不大可能,不必要,不提倡的。
先看例子一:
class SubRunnable implements Runnable { private CountDownLatch begin, end; private List<Integer> sublist; public SubRunnable(List<Integer> sublist, CountDownLatch begin,CountDownLatch end) { this.sublist = sublist; this.begin = begin; this.end = end; } @Override public void run() { try { begin.await(); if (sublist != null) { for (int i : sublist) { System.out.println("线程" + Thread.currentThread().getName() + ", i = " + i); } } } catch (InterruptedException e) { e.printStackTrace(); } finally{ System.out.println(System.currentTimeMillis() + ",线程" + Thread.currentThread().getName() + ",开始执行!"); end.countDown(); } } } public class BatchWithCountDownLatch { private static final int MAX = 3; private static void list(List<Integer> list) { if(list == null){ list = new ArrayList<Integer>(); } for(int i = 0 ;i < 1000;i++){ list.add(i); } } public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); list(list); //把list拆分成多个 int mod = list.size() % MAX; int threadCount = mod == 0 ? list.size() / MAX : list.size() / MAX + 1; ExecutorService executors = Executors.newFixedThreadPool(threadCount); CountDownLatch begin = new CountDownLatch(1); CountDownLatch end = new CountDownLatch(threadCount); for(int i = 0; i< threadCount;i++){ int subsize = (i + 1) * MAX; executors.execute(new SubRunnable(list.subList(i * MAX, subsize > list.size() ? list.size() : subsize),begin,end)); } System.out.println("开始 !"); begin.countDown(); long startTime = System.currentTimeMillis(); try { end.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("线程" + Thread.currentThread().getName() + "," + System.currentTimeMillis() + ", 所有线程已完成,开始进入下一步!"); System.out.println("花费时间 -> " + (System.currentTimeMillis() - startTime) + " ms"); } System.out.println("开始进入第二步操作! "); System.out.println("end! "); } }
这是根据jdk文档中的伪代码例程,编写的一个例子,我们完全可以将这个例程改为只使用一个CountDownLatch来实现之。经过测试,发现begin的引入对程序基本无用,当list是1000的数量级时,最先启动的线程仍然比最后启动的快几十毫秒左右;而不设置begin开始闭锁的程序,也是完全一样的情况。
例子二:
class SubRunnable implements Runnable { private CyclicBarrier cyclicBarrier; private List<Integer> sublist; public SubRunnable(List<Integer> sublist, CyclicBarrier cyclicBarrier) { this.sublist = sublist; this.cyclicBarrier = cyclicBarrier; } @Override public void run() { try { System.out.println(System.currentTimeMillis() + ",线程" + Thread.currentThread().getName() + ",开始执行!"); if(sublist != null){ for(int i : sublist){ System.out.println("线程" + Thread.currentThread().getName() + ", i = " + i); } } cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } } public class ReplaceCountDownLatch { private static final int MAX = 3; private static void list(List<Integer> list) { if(list == null){ list = new ArrayList<Integer>(); } for(int i = 0 ;i < 10;i++){ list.add(i); } } public static void main(String[] args) throws InterruptedException, BrokenBarrierException { List<Integer> list = new ArrayList<Integer>(); list(list); //把list拆分成多个 int mod = list.size() % MAX; int threadCount = mod == 0 ? list.size() / MAX : list.size() / MAX + 1; ExecutorService executors = Executors.newFixedThreadPool(threadCount); final CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount,new Runnable() { @Override public void run() { //根据jdkdoc里的描述,哪个线程最后运行完,就执行下面的代码。 System.out.println("线程" + Thread.currentThread().getName() + "," + System.currentTimeMillis() + ", 所有线程已完成,开始进入下一步!"); } }); for(int i = 0; i< threadCount;i++){ int subsize = (i + 1) * MAX; executors.execute(new SubRunnable(list.subList(i * MAX, subsize > list.size() ? list.size() : subsize),cyclicBarrier)); } cyclicBarrier.await(); executors.shutdown(); System.out.println("开始进入第二步操作! "); System.out.println("end! "); } }
使用栅栏CyclicBarrier实现和上面闭锁CountDownLatch相同的功能。
上一篇: HttpClient4.5 使用http连接池发送http请求深度示例
下一篇: 论架构师的职责