点击上方 好好学java ,选择 星标 公众号

重磅资讯、干货,第一时间送达
今日推荐:是时候扔掉Postman了,又一个被低估的IDEA插件出来了...个人原创+1博客:点击前往,查看更多
链接:https://segmentfault.com/a/1190000012234469

CountDownLatch

  • CountDownLatch 类位于 java.util.concurrent 包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

  • CountDownLatch类只提供了一个构造器:

public CountDownLatch(int count) {  };  //参数count为计数值
  • 然后下面这3个方法是CountDownLatch类中最重要的方法:

public void await() throws InterruptedException { };   //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { };  //将count值减1
  • 代码实现

package sychronized;import static net.mindview.util.Print.*;
import java.util.concurrent.*;class Task implements Runnable{private static int count = 0;private final int id = count++;final CountDownLatch latch ;public Task(CountDownLatch latch){this.latch = latch;}@Overridepublic void run(){try {print(this+"正在执行");TimeUnit.MILLISECONDS.sleep(3000);print(this+"执行完毕");latch.countDown();} catch (InterruptedException e) {print(this + " 被中断");}}@Overridepublic String toString() {return "Task-"+id;}
}public class Test {public static void main(String[] args) {final CountDownLatch latch = new CountDownLatch(2);ExecutorService exec = Executors.newCachedThreadPool();exec.execute(new Task(latch));exec.execute(new Task(latch));try {print("等待2个子线程执行完毕...");long start = System.currentTimeMillis();latch.await();long end = System.currentTimeMillis();print("2个子线程已经执行完毕 "+(end - start));print("继续执行主线程");}catch (InterruptedException e){print("主线程被中断");}exec.shutdown();}
}#输出结果:
等待2个子线程执行完毕...
Task-0正在执行
Task-1正在执行
Task-0执行完毕
Task-1执行完毕
2个子线程已经执行完毕 3049
继续执行主线程

CyclicBarrier

  • 字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。

  • CyclicBarrier类位于java.util.concurrent包下,CyclicBarrier提供2个构造器:

  • 参数parties指让多少个线程或者任务等待至barrier状态

  • 参数barrierAction为当这些线程都达到barrier状态时会执行的内容

public CyclicBarrier(int parties, Runnable barrierAction) {}
public CyclicBarrier(int parties) {}
  • 然后CyclicBarrier中最重要的方法就是 await 方法,它有2个重载版本:

  • 第一个版本比较常用,用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;

  • 第二个版本是让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。

public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
  • 代码展示

package sychronized;import java.util.Random;
import java.util.concurrent.*;
import static net.mindview.util.Print.*;class WriteTask implements Runnable{private static int count = 0;private final int id = count++;private CyclicBarrier barrier ;private static Random random = new Random(47);public WriteTask(CyclicBarrier cyclicBarrier) {this.barrier = cyclicBarrier;}@Overridepublic void run() {print(this+"开始写入数据...");try {TimeUnit.MILLISECONDS.sleep(random.nextInt(5000));      //以睡眠来模拟写入数据操作print(this+"写入数据完毕,等待其他线程写入完毕"+" "+System.currentTimeMillis());barrier.await();} catch (InterruptedException e) {print(this + "is interrupted!");}catch(BrokenBarrierException e){throw new RuntimeException(e);}print("所有任务写入完毕,继续处理其他任务... "+System.currentTimeMillis());}@Overridepublic String toString() {return getClass().getSimpleName()+"-"+id;}
}public class CyclicBarrierTest {public static void main(String[] args) {int N = 4;CyclicBarrier barrier  = new CyclicBarrier(N);ExecutorService exec = Executors.newCachedThreadPool();for(int i = 0; i < N; ++i){exec.execute(new WriteTask(barrier));}exec.shutdown();}
}#输出结果:
WriteTask-3 开始写入数据...
WriteTask-2 开始写入数据...
WriteTask-1 开始写入数据...
WriteTask-0 开始写入数据...
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512048648904
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512048650042
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512048650209
WriteTask-3 写入数据完毕,等待其他线程写入完毕 1512048652606
所有任务写入完毕,继续处理其他任务... 1512048652607
所有任务写入完毕,继续处理其他任务... 1512048652607
所有任务写入完毕,继续处理其他任务... 1512048652607
所有任务写入完毕,继续处理其他任务... 1512048652607
  • 如果说想在所有线程写入操作完之后,进行额外的其他操作可以为CyclicBarrier提供Runnable参数:

package sychronized;import java.util.Random;
import java.util.concurrent.*;
import static net.mindview.util.Print.*;class WriteTask implements Runnable{private static int count = 0;private final int id = count++;private CyclicBarrier barrier ;private static Random random = new Random(47);public WriteTask(CyclicBarrier cyclicBarrier) {this.barrier = cyclicBarrier;}@Overridepublic void run() {print(this+" 开始写入数据...");try {TimeUnit.MILLISECONDS.sleep(random.nextInt(5000));      //以睡眠来模拟写入数据操作print(this+" 写入数据完毕,等待其他线程写入完毕"+" "+System.currentTimeMillis());barrier.await();TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {print(this + "is interrupted!");}catch(BrokenBarrierException e){throw new RuntimeException(e);}print("所有任务写入完毕,继续处理其他任务... "+System.currentTimeMillis()+Thread.currentThread());}@Overridepublic String toString() {return getClass().getSimpleName()+"-"+id;}
}public class CyclicBarrierTest {public static void main(String[] args) {int N = 4;CyclicBarrier barrier  = new CyclicBarrier(N, new Runnable() {@Overridepublic void run() {print(Thread.currentThread());}});ExecutorService exec = Executors.newCachedThreadPool();for(int i = 0; i < N; ++i){exec.execute(new WriteTask(barrier));}exec.shutdown();}
}
#输出结果为:
WriteTask-3 开始写入数据...
WriteTask-1 开始写入数据...
WriteTask-2 开始写入数据...
WriteTask-0 开始写入数据...
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512049061954
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512049063092
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512049063261
WriteTask-3 写入数据完毕,等待其他线程写入完毕 1512049065657
Thread[pool-1-thread-4,5,main]
所有任务写入完毕,继续处理其他任务... 1512049065668Thread[pool-1-thread-2,5,main]
所有任务写入完毕,继续处理其他任务... 1512049065668Thread[pool-1-thread-1,5,main]
所有任务写入完毕,继续处理其他任务... 1512049065668Thread[pool-1-thread-4,5,main]
所有任务写入完毕,继续处理其他任务... 1512049065668Thread[pool-1-thread-3,5,main]

从结果可以看出,当四个线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。

  • 另外CyclicBarrier是可以重用的,看下面这个例子:

package sychronized;import java.util.Random;
import java.util.concurrent.*;
import static net.mindview.util.Print.*;class WriteTask implements Runnable{private static int count = 0;private final int id = count++;private CyclicBarrier barrier ;private static Random random = new Random(47);public WriteTask(CyclicBarrier cyclicBarrier) {this.barrier = cyclicBarrier;}@Overridepublic void run() {while (!Thread.interrupted()){print(this+" 开始写入数据...");try {TimeUnit.MILLISECONDS.sleep(random.nextInt(5000));      //以睡眠来模拟写入数据操作print(this+" 写入数据完毕,等待其他线程写入完毕"+" "+System.currentTimeMillis());barrier.await();TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {print(this + "is interrupted!");}catch(BrokenBarrierException e){throw new RuntimeException(e);}print("所有任务写入完毕,继续处理其他任务... "+System.currentTimeMillis());}}@Overridepublic String toString() {return getClass().getSimpleName()+"-"+id;}
}class CyclicBarrierManager implements Runnable{private CyclicBarrier barrier ;private ExecutorService exec;public CyclicBarrierManager(CyclicBarrier barrier, ExecutorService exec,int N){this.barrier = barrier ;this.exec = exec;for (int i = 0; i < N-1; ++i){exec.execute(new WriteTask(barrier));}}@Overridepublic void run(){while (!Thread.interrupted()){try {barrier.await();}catch (InterruptedException e){print(getClass().getSimpleName()+" 被中断了!");}catch (BrokenBarrierException e){throw new RuntimeException(e);}}}
}public class CyclicBarrierTest {public static void main(String[] args) throws Exception{int N = 4;CyclicBarrier barrier  = new CyclicBarrier(N);ExecutorService exec = Executors.newCachedThreadPool();exec.execute(new CyclicBarrierManager(barrier,exec,N));exec.shutdown();}
}#输出结果:
WriteTask-1 开始写入数据...
WriteTask-2 开始写入数据...
WriteTask-0 开始写入数据...
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512051484365
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512051485503
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512051488068
所有任务写入完毕,继续处理其他任务... 1512051488078
所有任务写入完毕,继续处理其他任务... 1512051488078
WriteTask-2 开始写入数据...
所有任务写入完毕,继续处理其他任务... 1512051488078
WriteTask-1 开始写入数据...
WriteTask-0 开始写入数据...
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512051488513
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512051489045
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512051489945
所有任务写入完毕,继续处理其他任务... 1512051489955
WriteTask-0 开始写入数据...
所有任务写入完毕,继续处理其他任务... 1512051489955
所有任务写入完毕,继续处理其他任务... 1512051489955
WriteTask-2 开始写入数据...
WriteTask-1 开始写入数据...
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512051490155
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512051494477
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512051494823
所有任务写入完毕,继续处理其他任务... 1512051494833
所有任务写入完毕,继续处理其他任务... 1512051494833
WriteTask-0 开始写入数据...
所有任务写入完毕,继续处理其他任务... 1512051494833
WriteTask-1 开始写入数据...
WriteTask-2 开始写入数据...
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512051494961
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512051496040
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512051498121
所有任务写入完毕,继续处理其他任务... 1512051498132
所有任务写入完毕,继续处理其他任务... 1512051498132
WriteTask-1 开始写入数据...
所有任务写入完毕,继续处理其他任务... 1512051498132

Semaphore

  • Semaphore翻译成字面意思为 信号量,Semaphore 可以同时让多个线程同时访问共享资源,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

  • Semaphore类位于java.util.concurrent包下,它提供了2个构造器:

public Semaphore(int permits) {          //参数permits表示许可数目,即同时可以允许多少线程进行访问sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {    //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}
  • 下面说一下Semaphore类中比较重要的几个方法,首先是acquire()、release()方法:

public void acquire() throws InterruptedException {  }     //获取一个许可
public void acquire(int permits) throws InterruptedException { }    //获取permits个许可
public void release() {}          //释放一个许可
public void release(int permits) {}    //释放permits个许可
  • acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。

  • release()用来释放许可

  • 注意,在释放许可之前,必须先获获得许可。

这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法:

public boolean tryAcquire() { };    //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };  //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
package sychronized;
import java.util.Random;
import java.util.concurrent.*;
import static net.mindview.util.Print.*;class Worker implements Runnable{private static int count = 0;private final int id = count++;private int finished = 0;private Random random = new Random(47);private Semaphore semaphore;public Worker(Semaphore semaphore){this.semaphore = semaphore;}@Override public void run(){try {while (!Thread.interrupted()){semaphore.acquire();print(this+" 占用一个机器在生产...   ");TimeUnit.MILLISECONDS.sleep(random.nextInt(2000));synchronized (this){print(" 已经生产了"+(++finished)+"个产品,"+"释放出机器");}semaphore.release();}} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic String toString() {return getClass().getSimpleName()+"-"+id;}
}public class SemaphoreTest {public static void main(String[] args) {int N = 8;            //工人数Semaphore semaphore = new Semaphore(5); //机器数目ExecutorService exec = Executors.newCachedThreadPool();for (int i = 0; i < N; ++i){exec.execute(new Worker(semaphore));}exec.shutdown();}
}

总结

CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

  • CountDownLatch 一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

  • CyclicBarrier 一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

  • CountDownLatch 是不能够重用的,而 CyclicBarrier 是可以重用的。

Semaphore 其实和锁有点类似,它一般用于控制对 某组 资源的访问权限,而锁是控制对 某个 资源的访问权限。

CountDownLatch、CyclicBarrier、Semaphore的区别,你知道吗?相关推荐

  1. (面经总结)一篇文章带你完整复习 Java 中并发关键字(CountDownLatch/CyclicBarrier/Semaphore/Volatile)

    文章目录 一.倒计数器:CountDownLatch 二.循环栅栏:CyclicBarrier 三.信号量:Semaphore 四.volatile 关键字的作用 一.倒计数器:CountDownLa ...

  2. JUC介绍--常用辅助类(CountDownLatch CyclicBarrier Semaphore)

    CountDownLatch减法计数器 每次有线程调用countDownLatch.countDown()数量就-1 数量减到0时,countDownLatch.await()会被唤醒,继续向下执行 ...

  3. CountDownLatch CyclicBarrier Semaphore

    CountDownLacth CountDownLacth(倒计数锁存器)到底有什么用呢?我们来看下面这个场景. 我们又有小片片要开始拍摄了,这个小片片需要5个演员来演,开演之前,导演需要这5个演员全 ...

  4. CountDownLatch和Semaphore使用场景

    CountDownLatch CountDownLatch位于java.util.concurrent包下,利用它可以实现类似计数器的功能.比如有一个任务A,它要等到其它3任务完成才能执行,此时就可以 ...

  5. 【嵌入式操作系统】FreeRTOS信号量mutex和semaphore的区别

    今天学习信号量mutex和semaphore的区别,找到了正点原子的博客介绍,讲的挺详细的.建议大家阅读 转载自:https://blog.csdn.net/nippon1218/article/de ...

  6. Java并发工具类:CountDownLatch、Semaphore、CyclicBarrier、Exchanger、Phaser

    本文目录: 1.CountDownLatch(闭锁) 1.CountDownLatch 例子 2.CyclicBarrier(循环栅栏) 1.CyclicBarrier 例子 2.CountDownL ...

  7. 《Java 7 并发编程指南》学习概要 (3)Semaphore, CountDownLatch, CyclicBarrier , Phaser, Exchanger...

    1.Semaphore  信号量 Semaphore(信号量)是一个控制访问多个共享资源的计数器. 当一个线程想要访问某个共享资源,首先,它必须获得semaphore.如果semaphore的内部计数 ...

  8. Countdownlatch、CyclicBarrier、join区别

    1.join 模拟现在有三个worker,采用join的方法控制: package org.pbccrc.org.pbccrc.thread;public class Test {public sta ...

  9. CyclicBarrier和CountDownLatch的用法与区别

    前言 CyclicBarrier和CountDownLatch这两个工具都是在java.util.concurrent包下,并且平时很多场景都会使用到. 本文将会对两者进行分析,记录他们的用法和区别. ...

最新文章

  1. 推荐一位玩自动化的 Python 爱好者
  2. 为了用户体验,不要做浏览器兼容
  3. 六、MySql索引分类
  4. UNICODE转多字节
  5. python PyQt5 QComboBox类(下拉列表框、组合下拉框)
  6. 【excel】日期函数DateDif
  7. 大佬对协程以及try except的详细解释
  8. Entity Framework Code First模式基础知识及入门实例01
  9. 小技巧 ----- 关于Java中的System.arraycopy()
  10. 恒生电子offer含金量_收获8个Offer,来给大家分享备战和面试经验
  11. 医疗管理系统HIS源码
  12. 分集与复用,分集用于抵抗信道衰落,复用用于提升系统容量
  13. 用react制作半圆形进度条
  14. ios label内字体置顶_IOS_Vertically align UILabel文本置顶 | 学步园
  15. elementui的表单验证踩坑-动态绑定输入框required后只显示英文提示
  16. 福特汉姆计算机专业,福特汉姆大学计算机如何
  17. 您的美团账户,美团互助未经客户同意自动扣费0.01元是什么情况
  18. SQL练习题_ 查询每个部门工资最高的前两名的姓名和部门名称【多测师_何sir】
  19. 美国top10计算机phd申请案例,申请美国计算机PHD全奖应该怎么做
  20. 笔记本连接老显示器一直弹出‘输出信号超出范围“终于解决了--通过删除显示器的注册表

热门文章

  1. (转)几种流行的JS框架的选择
  2. oracle adg的特点是什么,Oracle12c ADG新特性
  3. 区块链BaaS云服务(29) 溪塔科技 CITA-Cloud 二
  4. 基于区块链的健康链系统设计与实现(6)结束语
  5. 初等数论--同余方程--同余方程组:中国剩余定理
  6. linux 中ans 用法,JSON简介以及用法汇总
  7. 基于异或,取反和循环移位实现一个简单的加密解密函数
  8. 【Win32汇编】__declspec(naked)裸函数
  9. androidstuido_schooltest_6_media_service
  10. angr学习笔记(5)(栈符号化)