首先我们来实现一个功能:当我们启动一个系统的时候需要初始化许多数据,这时候我们可能需要启动很多线程来进行数据的初始化,只有这些系统初始化结束之后才能够启动系统。其实在Java的类库中已经提供了Semaphore、CountDownLatch、CyclicBarrier这3个类来帮我们实现这样类似的功能了。

一、信号灯 Semaphore

Semaphore sp = new Semaphore(int permits) 接受一个整数型的参数,表示有几盏灯。线程可以通过semaphore.acquire()获取一盏信号灯,通过semaphore.release()释放。它与 Lock 的区别就是 Lock 只能是一个锁,而 Semaphore 更像是多个锁的一个集合,像一个阻塞队列一样,当队列中的锁用完了,而你又需要锁的时候,你就必须等待其他的线程释放锁。

下面我们声明了只有 1 个灯的信号灯,然后启动 3 个线程同时去获取信号灯,另外还启动了 1 个线程每 2 秒就释放一次信号灯。

  1. /**
  2. * Semaphore 实现信号灯
  3. * @author chenyr
  4. * @time 2014-12-24 下午08:13:32
  5. * All Rights Reserved.
  6. */
  7. public class Semaphore1 {
  8.  
  9. public static void main(String[] args) {
  10. final Semaphore sp = new Semaphore(1); //只声明一盏信号灯
  11. //业务线程1
  12. new Thread(new Runnable(){
  13. public void run(){
  14. try{
  15. System.out.println(Thread.currentThread().getName() + "准备获取信号灯-A");
  16. sp.acquire(); //获取信号灯
  17. System.out.println(Thread.currentThread().getName() + "已获取信号灯-A");
  18. }catch(Exception e){
  19. e.printStackTrace();
  20. }
  21. }
  22. }).start();
  23. //业务线程2
  24. new Thread(new Runnable(){
  25. public void run(){
  26. try{
  27. System.out.println(Thread.currentThread().getName() + "准备获取信号灯-B");
  28. sp.acquire(); //获取信号灯
  29. System.out.println(Thread.currentThread().getName() + "已获取信号灯-B");
  30. }catch(Exception e){
  31. e.printStackTrace();
  32. }
  33. }
  34. }).start();
  35. //业务线程3
  36. new Thread(new Runnable(){
  37. public void run(){
  38. try{
  39. System.out.println(Thread.currentThread().getName() + "准备获取信号灯-C");
  40. sp.acquire(); //获取信号灯
  41. System.out.println(Thread.currentThread().getName() + "已获取信号灯-C");
  42. }catch(Exception e){
  43. e.printStackTrace();
  44. }
  45. }
  46. }).start();
  47. //检查线程
  48. new Timer().schedule(new TimerTask(){
  49. public void run(){
  50. System.out.println("每10s释放一次信号灯");
  51. sp.release();
  52. System.out.println("信号灯已释放");
  53. }
  54. }, 2000, 2000); //每2秒释放一次信号灯
  55. }
  56. }

执行结果如下:

  1. Thread-0准备获取信号灯-A
  2. Thread-0已获取信号灯-A
  3. Thread-1准备获取信号灯-B
  4. Thread-2准备获取信号灯-C
  5. 2s释放一次信号灯
  6. 信号灯已释放
  7. Thread-1已获取信号灯-B
  8. 2s释放一次信号灯
  9. 信号灯已释放
  10. Thread-2已获取信号灯-C
  11. 2s释放一次信号灯
  12. 信号灯已释放

从结果可以看出一开始 Thread-0 获得了锁,Thread-1 和 Thread-2 都在等待获取,直到检查线程 2 秒后释放信号灯,Thread-1 才获得了信号灯。而 Thread-2 是在检查线程再次释放锁的时候获取到的。

二、倒计时门栓 CountDownLatch

接受一个整数型的参数,可以通过countDownLatch.countDown()减少一个计时,countDownLatch.await()进行线程等待,等到countDownLatch中的计数到0之后就会恢复执行。CountDownLatch 与 Semaphore 的作用完全不同,CountDownLatch 是类似于集合点的一个类,当调用者到达一个数目就会触发一些操作。而 Semaphore 是一个类似于锁队列的东西,锁用完了就是用完了,而不会触发操作。

下面我们模拟跑步比赛的例子,用 3 个线程分别模拟 3 个运动员。而这其中有 3 个节点,分别是:

1、要等 3 个运动员都准备好了,裁判才能发开跑命令

2、3 个运动员要等裁判发令才能跑

3、裁判员要等 3 个运动员都到终点了才能宣布成绩

这 3 个时间点我们分别用一个 CountDownLatch 对象来表示,具体实现如下。

  1. /**
  2. * CountDownLatch同步工具
  3. * 实例:模拟运动员跑步的例子(等待裁判发令,3个运动员才跑。等到3个运动员都跑完了,裁判才宣布成绩)
  4. * @author chenyr
  5. * @time 2014-12-25 下午07:49:25
  6. * All Rights Reserved.
  7. */
  8. public class CountDownLatch1 {
  9.  
  10. public static void main(String[] args) {
  11. ExecutorService service = Executors.newCachedThreadPool();
  12.  
  13. //要等到3个运动都准备好了,裁判才能命令
  14. final CountDownLatch waitCd = new CountDownLatch(3);
  15. //裁判发1次命令,运动员就开始跑
  16. final CountDownLatch orderCd = new CountDownLatch(1);
  17. //要等到3个运动员到达终点,裁判才公布成绩
  18. final CountDownLatch scoreCd = new CountDownLatch(3);
  19.  
  20. //模拟3个运动员
  21. for(int i = 1; i <= 3; i++){
  22. final int count = i;
  23. Runnable runnable = new Runnable(){
  24. public void run(){
  25. try{
  26. System.out.println("运动员" + count + "站在起跑线准备比赛了!");
  27. waitCd.countDown(); //准备好
  28. orderCd.await();
  29.  
  30. Thread.sleep((long)(Math.random() * 10000)); //模拟运动员隔多久听到命令
  31. System.out.println("运动员" + count + "听到开跑命令,开跑!");
  32.  
  33. Thread.sleep((long)(Math.random() * 10000)); //模拟运动员用多长时间跑到终点
  34. System.out.println("运动员" + count + "跑到了终点!");
  35. scoreCd.countDown(); //跑到终点
  36. }catch(Exception e){
  37. e.printStackTrace();
  38. }
  39. }
  40. };
  41. service.execute(runnable);
  42. }
  43.  
  44. //模拟裁判
  45. Runnable runnable = new Runnable(){
  46. public void run(){
  47. try{
  48. System.out.println("裁判已到位,正在等待运动员做好准备!");
  49. waitCd.await();
  50. System.out.println("所有运动员已经就位,裁判准备发令!");
  51. Thread.sleep((long)(Math.random() * 10000)); //模拟裁判的准备时间
  52. System.out.println("裁判:比赛开始! 跑!跑!跑!");
  53. orderCd.countDown(); //开跑
  54. scoreCd.await();
  55. System.out.println("所有运动员已经到达终点,裁判宣布成绩!");
  56. }catch(Exception e){
  57. e.printStackTrace();
  58. }
  59. }
  60. };
  61. service.execute(runnable);
  62.  
  63. service.shutdown();
  64. }
  65. }

运行结果如下:

  1. 运动员2站在起跑线准备比赛了!
  2. 运动员3站在起跑线准备比赛了!
  3. 裁判已到位,正在等待运动员做好准备!
  4. 运动员1站在起跑线准备比赛了!
  5. 所有运动员已经就位,裁判准备发令!
  6. 裁判:比赛开始! 跑!跑!跑!
  7. 运动员1听到开跑命令,开跑!
  8. 运动员2听到开跑命令,开跑!
  9. 运动员1跑到了终点!
  10. 运动员3听到开跑命令,开跑!
  11. 运动员2跑到了终点!
  12. 运动员3跑到了终点!
  13. 所有运动员已经到达终点,裁判宣布成绩!

三、栅栏  CyclicBarrier

CyclicBarrier cb = new CyclicBarrier(int parties) 接受一个整数型的参数。线程可以通过cb.await()等待,只要正在等待的线程数目达到设定的参数,所有等待的线程就会恢复执行。CyclicBarrier 与 CountDownLatch 相似,都是要达到一样的人数才可以执行某些操作,只不过 CountDownLatch 是减操作,而 CyclicBarrier 是加操作。与 CyclicBarrier 相似的事件是集合点,即我们 5 个人周末一起去爬山,我们大家都要在某个地方等 5 个人到齐了再出发。

下面设置了两个集合点,只有当全部人到齐了第一个集合点之后,才会继续前往下一个集合点。

  1. /**
  2. * CyclicBarrier同步工具
  3. * 等待所有线程到达之后再继续执行
  4. * @author chenyr
  5. * @time 2014-12-25 下午07:30:00
  6. * All Rights Reserved.
  7. */
  8. public class CyclicBarrier1 {
  9.  
  10. public static void main(String[] args) {
  11. ExecutorService service = Executors.newCachedThreadPool();
  12. final CyclicBarrier cb = new CyclicBarrier(3); //一共要等到几个线程才继续执行
  13. for(int i = 1; i <= 3; i++){
  14. Runnable runnable = new Runnable(){
  15. public void run(){
  16. try{
  17. Thread.sleep((long)(Math.random() * 10000));
  18. System.out.println(Thread.currentThread().getName() + "已经到达集合点A,正在等待。目前已有" + (cb.getNumberWaiting() + 1) + "个线程在等待" );
  19. cb.await();
  20. System.out.println("全部线程到达A集合点");
  21.  
  22. Thread.sleep((long)(Math.random() * 10000));
  23. System.out.println(Thread.currentThread().getName() + "已经到达集合点B,正在等待。目前已有" + (cb.getNumberWaiting() + 1) + "个线程在等待" );
  24. cb.await();
  25. System.out.println("全部线程到达B集合点");
  26. }catch(Exception e){
  27. e.printStackTrace();
  28. }
  29. }
  30. };
  31. service.execute(runnable);
  32. }
  33. }
  34. }

运行结果如下:

  1. pool-1-thread-2已经到达集合点A,正在等待。目前已有1个线程在等待
  2. pool-1-thread-1已经到达集合点A,正在等待。目前已有2个线程在等待
  3. pool-1-thread-3已经到达集合点A,正在等待。目前已有3个线程在等待
  4. 全部线程到达A集合点
  5. 全部线程到达A集合点
  6. 全部线程到达A集合点
  7. pool-1-thread-1已经到达集合点B,正在等待。目前已有1个线程在等待
  8. pool-1-thread-3已经到达集合点B,正在等待。目前已有2个线程在等待
  9. pool-1-thread-2已经到达集合点B,正在等待。目前已有3个线程在等待
  10. 全部线程到达B集合点
  11. 全部线程到达B集合点
  12. 全部线程到达B集合点

四、CountDownLatch 和 CyclicBarrier 的区别

一般情况下对于两个非常相似的类,我们一般都会想当然地去把他们进行类比。对于 CountDownLatch 和 CyclicBarrier 两个类,我们可以看到CountDownLatch 类都是一个类似于集结点的概念,很多个线程做完事情之后等待其他线程完成,全部线程完成之后再恢复运行。不同的是CountDownLatch 类需要你自己调用 countDown() 方法减少一个计数,然后调用 await() 方法即可。而 CyclicBarrier 则直接调用 await() 方法即可。

所以从上面来看,CountDownLatch 更倾向于多个线程合作的情况,等你所有东西都准备好了,我这边就自动执行了。而 CyclicBarrier 则是我们都在一个地方等你,大家到齐了,大家再一起执行。

参考资料:http://www.cnblogs.com/dolphin0520/p/3920397.html

Java并发编程:Semaphore、CountDownLatch、CyclicBarrier的更多相关文章

  1. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...

  2. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore (总结)

    下面对上面说的三个辅助类进行一个总结: 1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同: CountDownLatch一般用于某个线程A等待 ...

  3. 14、Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...

  4. 【转】Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore   Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在j ...

  5. Java并发编程Semaphore

    信号量 信号量类Semaphore,用来保护对唯一共享资源的访问.一个简单的打印队列,并发任务进行打印,加入信号量同时之能有一个线程进行打印任务 . import java.util.concurre ...

  6. Java并发编程:CountDownLatch、CyclicBarrier和 Semaphore

    原文出处: 海子 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅 ...

  7. Java并发编程:CountDownLatch、CyclicBarrier和 Semaphore[转]

    [转载]http://www.cnblogs.com/dolphin0520/p/3920397.html 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDow ...

  8. Java并发编程: CountDownLatch、CyclicBarrier和 Semaphore

    java 1.5提供了一些非常有用的辅助类来帮助并发编程,比如CountDownLatch,CyclicBarrier和Semaphore. 1.CountDownLatch –主线程阻塞等待,最后完 ...

  9. java 并发工具类CountDownLatch & CyclicBarrier

    一起在java1.5被引入的并发工具类还有CountDownLatch.CyclicBarrier.Semaphore.ConcurrentHashMap和BlockingQueue,它们都存在于ja ...

  10. Java并发编程:CountDownLatch、CyclicBarrier和 Semaphore , Condition

    http://www.importnew.com/21889.html 1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同: CountDown ...

随机推荐

  1. 关于boostrap的thead固定tbody滚动

    原文地址:http://blog.csdn.net/bbsyi/article/details/51126041# 1 <!DOCTYPE html> 2 <html> 3 & ...

  2. Codeforces Round #383 Div 1题解

    第一次打Div 1,感觉还是挺难的..把基础题打完就日常划水了.... [A. Arpa's loud Owf and Mehrdad's evil plan](http://codeforces.c ...

  3. github fork, star and watch

    1 git fork git clone原版本的话,只有读权限,是不能直接把修改提交到服务器的. git fork会创建一个副本,然后就可以在这个上面进行开发了,开发了之后可以通过pull reque ...

  4. PHP字符串三种定义方式

    PHP的字符串有三种定义方式:单引号 .双引号 .定界符  1.单引号:指定一个简单字符串的最简单的方法是用单引号(字符 ')括起来. 在被单引号括起来的字符串中,要再表示一个单引号,需要用反斜线(\ ...

  5. 读书笔记 effective c++ Item 26 尽量推迟变量的定义

    1. 定义变量会引发构造和析构开销 每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入了调用构造函数的开销,当离开变量的作用域之后,你引入了调用析构函数的开销.对未使用到的变量同样会产生开 ...

  6. 2017-3-2 C#链接数据库实现登陆

    只是链接一个数据库就有好多的知识:) 实际操作下来,主要是两种登陆方式: 1.Windows的身份验证: 2.Sql Sever的身份验证: 两种的方法不同,但是主要是通过复制创建数据库的字符串来链接 ...

  7. 用C#代码实现类似QQ窗体的“上、左、右”停靠功能

    大家都知道QQ有一个自动停靠功能,即“上.左.右”,当你把窗体拖到屏幕边缘,然后移开鼠标它会自动缩放,然后只显示一小小点出来,我们仔细观察会发现其实它只露3像素左右的边缘,当你鼠标移上去它又会伸出来, ...

  8. Android中使用findViewByMe提升组件查找效率

    1.引出 安卓初学者一般在写android Activity的时候总是会在onCreate方法中加上setContentView方法来加载layout,通过findViewById来实现控件的绑定,刚 ...

  9. Linux下使用ssh密钥实现无交互备份

    服务器A(主) 192.168.1.120 服务器B(从) 192.168.1.130 需求:服务器B定期拉取服务器A的数据并备份. 实现方式: 一.备份服务器B安装rsync 1)查看是否安装 rp ...

  10. 有关ospf抓包

    有关ospf抓包 1.相关的路由器为这样子的: 路由器都运行了ospf: 抓包的链路为GE0/0/2 , 2.抓包图: 从图上我们可以看到,protocol info 这一栏里面出现了: hello ...