首页 > 其他分享 >CountDownLatch与CyclicBarrier分析及其区别

CountDownLatch与CyclicBarrier分析及其区别

时间:2023-03-17 21:02:13浏览次数:39  
标签:区别 await System 线程 doneSignal CountDownLatch println CyclicBarrier


相同点:
CountDownLatch、CyclicBarrier均在jdk1.5引入的,并且都在concurrent包(用于并发处理)下。均用于实现线程同步。

差异点:
1 CountDownLatch计数器只能使用一次。CyclicBarrier则可以调用其reset()方法进行重置多次使用(在计算错误时可重置后再计算)。
2 CountDownLatch使用countDown()+await()进行处理,需要通过countDown的次数到设置的次数,其await()才不会阻塞,往往是一个主线程中使用CountDownLatch然后控制所有子线程。CyclicBarrier的同步屏障是针对对应的子线程的,但同时设置了new CyclicBarrier(N)也需要对应N次的线程(部分主子线程)来执行await(),才能继续执行await()后面的代码。


CountDownLatch分析:

使用场景:用于多线程计算。如分开线程计算Excel中每个Sheet,最后再合并结果。


用法说明,具体用法如下两个:

CountDownLatch相当于一个闸门控制器,可通过构造方法初始化闸门的数量,然后通过countDown对一个个闸门进行开闸。使用await()进行阻塞,最后所有闸门都开启了await()就不会进行阻塞了。

CountDownLatch与CyclicBarrier分析及其区别_同步


======================================>可以说是等待一组线程执行完成。

1 源码第一种例子分析如下:

public class Demo02 {
private static final Integer N = 100000;
@Test
public void test1() throws Exception {
CountDownLatch startSignal = new CountDownLatch(1); //启动信号
CountDownLatch doneSignal = new CountDownLatch(N); //工作信号
//创建N个线程
for (int i = 0; i < N; ++i)
new Thread(new Worker(startSignal, doneSignal)).start();


System.out.println("主线程=>countDown前");
//"启动信号"触发开闸,因为只有一个闸,停止await()方法的阻塞。
startSignal.countDown();
System.out.println("主线程=>await前");
//"工作信号"等待其对应的所有闸门(即N个闸门都开闸),才会停止await()方法的阻塞。
doneSignal.await();
System.out.println("主线程=>await后");
}
}


class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;


Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}


public void run() {
try {
//"启动信号"在这里阻塞,等待启动信号的所有闸门都开了(即触发对应的次数的countDown)
startSignal.await();
doWork();
//"工作信号"在对应的子线程执行countDown()开一个闸门,等到所有子线程执行完就会开完N个闸门,这时候"工作信号"的await()就不会继续阻塞
doneSignal.countDown();
} catch (Exception ex) {
} // return;
}


private void doWork() {
System.out.println(Thread.currentThread().getName());
}
}

2 源码中第二个例子使用线程池异步执行新的线程并等待所有执行完毕后await()才不阻塞:

public void test2() throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(N);
Executor e = Executors.newFixedThreadPool(2);


//创建并异步启动并执行新的线程
for (int i = 0; i < N; ++i)
e.execute(new WorkerRunnable(doneSignal, i));
//"工作信号"调用await()方法阻塞,直到所有的闸门开启才停止阻塞。
doneSignal.await();
System.out.println("执行完毕");
}
class WorkerRunnable {
.............
public void run() {
try {
doWork(i);
//线程进入线程池,异步执行,每执行完对应的逻辑,会将对应砸门(countDown())开启
doneSignal.countDown();
} catch (Exception ex) {
} // return;
}
.............
}


上面说了CountDownLatch使用的基本方法和原理,那它的锁是怎么实现的?
翻其源码可见有一个静态内部类如下:
private static final class Sync extends AbstractQueuedSynchronizer {............}

注:如上可见CountDownLatch是借助AQS实现的锁机制,具体见AQS锁的文章(​​javascript:void(0)​​),更多关于AQS的分析文章待续。


CyclicBarrier分析:

应用场景:多线程计算数据,如分开线程计算Excel中每个Sheet,最后再合并结果。


用法说明,其用法如下:

对于CountDownLatch,这个也是一个实现线程同步的类。但区别是CyclicBarrier为每个线程都设置了一个内存屏障,只有所有达到了同步屏障才能统一通过其await()方法,进入到await()后面执行的代码的操作,对应县城的await()才不会阻塞。

CountDownLatch与CyclicBarrier分析及其区别_await()_02


======================================>可以说是一组线程相互等待。

实例用法如下:

private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);  //设置五个同步屏障,即需要五个线程都执行了await()才允许通过


@Test
public void test3() throws Exception {
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ",停止当前线程内存屏障前");//①
cyclicBarrier.await(); //解除当前子线程屏障
System.out.println(Thread.currentThread().getName() + ",停止当前线程内存屏障后");//②
} catch (Exception ignored) {}
}
}).start();
}
//停止掉主线程的一个内存屏障
System.out.println(Thread.currentThread().getName() + ",停止主线程内存屏障前");//④
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + ",停止主线程内存屏障后");//⑥
System.out.println("执行完毕");
}

注:以上操作是永远都不可能执行到②⑥,因为for里面只执行了三个线程对应通过了三个同步屏障,主线程执行了一次await()通过一个同步屏障,加起来<定义的五个同步屏障。所以所有线程的await()后面的代码均不可继续执行,阻塞在那里了。(解决办法:将new CyclicBarrier(5)改为new CyclicBarrier(4)这样同步屏障数量就刚好跟线程执行await()的数量对上了)。
 

标签:区别,await,System,线程,doneSignal,CountDownLatch,println,CyclicBarrier
From: https://blog.51cto.com/u_13854513/6128392

相关文章

  • 爬虫相关 https与http区别、bs4模块 遍历文档树、搜索文档树、find的其他参数、css选
    http与https的区别http和https的区别https=http+ssl/tslhttp版本区别0.9:底层基于tcp,每次http请求,都是建立一个tcp连接,三次握手,请求结束需要......
  • String、StringBuffer、StringBuilder区别与联系
      1.String类是不可变类,即一旦一个String对象被创建后,包含在这个对象中的字符序列是不可改变的,直至这个对象销毁。2.StringBuffer类则代表一个字符序列可变的字符串......
  • String、StringBuffer、StringBuilder区别与联系
      1.String类是不可变类,即一旦一个String对象被创建后,包含在这个对象中的字符序列是不可改变的,直至这个对象销毁。2.StringBuffer类则代表一个字符序列可变的字符串......
  • 直流充电桩和交流充电桩有什么区别?
    新能源汽车大家并不陌生,然而对充电桩可能不太了解,充电桩是利用充电接口,采取传导方式,为电动汽车提供电能的装置,主要分为立柱式、壁挂式和便携式三种,具有计费、通讯和安全防护......
  • 正向代理和反向代理的区别
    1、正向代理正向代理是基于客户端的代理,其用意就跟家用光猫一样,你的所有请求都是通过光猫,再由光猫进行转发请求,这就是正向代练,可以让你的真实ip地址隐藏掉,服务端没办......
  • 请你谈谈关于IO同步、异步、阻塞、非阻塞的区别
    对于一个networkIO(这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process(orthread),另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个......
  • HashMap和HashTable的区别?
    HashMap不是线程安全的,HashTable是线程安全的。HashMap允许nullkey和nullvalue,而HashTable不允许HashMap把Hashtable的contains方法去掉了,改成containsV......
  • ArrayList 和 Vector 的区别是什么?
    同步性Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用Array......
  • Restful、SOAP、RPC、SOA、微服务之间的区别
    内容大纲:1.介绍Restful、SOAP、RPC、SOA以及微服务2.重点谈谈SOA与微服务的区别3.以及为什么要使用微服务架构什么是RestfulRestful是一种架构设计风格,提供了设计原......
  • 计算,存储,网络虚拟化区别
     计算虚拟化1.什么是虚拟化什么是虚拟化:虚拟化是一种技术,将物理设备进行逻辑化,转化成文件夹或文件,实现软硬件解耦好处:    1.提高资源利用率   ......