在多线程的场景下,有些并发流程需要人为来控制,在JDK的并发包里提供了几个并发工具类:CountDownLatch、CyclicBarrier、Semaphore。

一、CountDownLatch

 1 import java.util.concurrent.CountDownLatch;
 2
 3
 4 public class CountDownLatchTest
 5 {    //设置N为2
 6     static CountDownLatch c = new CountDownLatch(2);
 7     public static void main(String[] args) throws Exception
 8     {
 9         Thread t1 = new Thread(new Runnable()
10         {
11
12             @Override
13             public void run()
14             {
15                 System.out.println("1");           //将N减1
16                 c.countDown();
17             }
18         });
19
20         Thread t2 = new Thread(new Runnable()
21         {
22
23             @Override
24             public void run()
25             {
26                 System.out.println("2");
27                 c.countDown();
28             }
29         });
30         t1.start();
31         t2.start();      //让当前线程等待,直到计数N为0
32         c.await();
33         System.out.println("3");
34     }
35 }

执行结果:

1
2
3

这里会存在两种结果:123或者213,但是绝对不会出现3打印在1、2前面的。

new CountDownLatch(2);

这个CountDownLatch的构造函数接收一个int类型的参数作为计数器,N表示阻塞的线程必须等待N次countDown才能执行。

每次调用CountDownLatch的countDown方法时,N就会减1,而这个方法可以使用在任何地方,这里的N点可以是N个线程,也可以是一个线程中N个步骤。

而CountDownLatch的await方法则会阻塞当前线程,直到N为0的时候才能执行。

我们将上面的程序改造下,让线程中有两个打印动作,并且第二个动作前线程休眠一段时间:

 1 import java.util.concurrent.CountDownLatch;
 2
 3
 4 public class CountDownLatchTest
 5 {
 6     static CountDownLatch c = new CountDownLatch(2);
 7     public static void main(String[] args) throws Exception
 8     {
 9         Thread t1 = new Thread(new Runnable()
10         {
11
12             @Override
13             public void run()
14             {
15                 System.out.println("1");
16                 c.countDown();
17                 try
18                 {
19                     Thread.sleep(500);
20                     System.out.println("2");
21                 }
22                 catch (InterruptedException e)
23                 {
24                     e.printStackTrace();
25                 }
26             }
27         });
28
29         Thread t2 = new Thread(new Runnable()
30         {
31
32             @Override
33             public void run()
34             {
35                 System.out.println("3");
36                 c.countDown();
37             }
38         });
39         t1.start();
40         t2.start();
41         c.await();
42         System.out.println("4");
43     }
44 }

执行结果:

1
3
4
2

这个结果是由于在打印完1、3之后,N已经变化为0,主线程执行打印4,由于线程1休眠,所以2最后才打印。

在上面的程序中,如果将N设置为3,则主线程中的打印4永远不会执行,因为没有N永远只会到1而不会减少到0.

在这里我们想起了线程的join方法,这个方法也是可以阻塞当前线程,等待某线程执行完成。通过对比,我们可以发现使用CountDownLatch这个工具类更灵活,因为countDown可以用在任何线程的任何地方。

CountDownLatch适合一个大任务拆分成多个小任务,然后在所有子任务完成后,通知其他的后续操作开始执行。

二、同步屏障CyclicBarrier

CyclicBarrier默认的构造方法CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达屏障,然后当前线程被阻塞,直到被拦截的线程全部都到达了屏障,然后前面被阻塞的线程才能开始执行,否则会被一直阻塞。

 1 public class CyclicBarrierTest
 2 {
 3     static CyclicBarrier c = new CyclicBarrier(3);
 4
 5     public static void main(String[] args)
 6         throws Throwable, BrokenBarrierException
 7     {
 8         Thread t1 = new Thread(new Runnable()
 9         {
10             @Override
11             public void run()
12             {
13                 try
14                 {
15                     c.await();
16                 }
17                 catch (InterruptedException e)
18                 {
19                     e.printStackTrace();
20                 }
21                 catch (BrokenBarrierException e)
22                 {
23                     e.printStackTrace();
24                 }
25                 System.out.println("1");
26
27             }
28         });
29         Thread t2 = new Thread(new Runnable()
30         {
31             @Override
32             public void run()
33             {
34                 try
35                 {
36                     c.await();
37                 }
38                 catch (InterruptedException e)
39                 {
40                     e.printStackTrace();
41                 }
42                 catch (BrokenBarrierException e)
43                 {
44                     e.printStackTrace();
45                 }
46                 System.out.println("2");
47             }
48         });
49
50         t1.start();
51         t2.start();
52         c.await();
53         System.out.println("3");
54     }
55 }

执行结果:

3
1
2

上述中被屏障拦截的线程有3个,其中线程1和线程2执行的时候先到达屏障,然后被阻塞,主线程执行第52行到达屏障,至此阻塞的三个线程全部到达屏障,然后阻塞的线程可以去竞争CPU开始执行。

如果将拦截的线程数修改为4:

static CyclicBarrier c = new CyclicBarrier(4);

这样的话被拦截的线程数有4个,但是只有三个线程调用await方法告诉CyclicBarrier,我到达了屏障。所以这三个线程都会被阻塞。

另外还有一点就是CyclicBarrier的计数器可以重置,例如设置的是拦截线程数量为2,但是有3个线程调用了await()方法表示到达了屏障,这个时候会出现最先达到屏障的两个线程顺利执行完毕,而最后到达的第三个线程则一直被阻塞,因为它等不到另外一个线程到达屏障了。

而如果拦截线程的数量依旧为2,但是有4个线程调用了await()方法,那么这4个线程是分两批执行的,前两个线程满足拦截的线程数,到达屏障后放行;然后CyclicBarrier的计数器重置,后面两个线程到达屏障后放行。

 1 import java.util.concurrent.BrokenBarrierException;
 2 import java.util.concurrent.CyclicBarrier;
 3
 4 import sun.java2d.SunGraphicsEnvironment.T1Filter;
 5
 6
 7 public class CyclicBarrierTest
 8 {
 9     static CyclicBarrier c = new CyclicBarrier(2);
10
11     public static void main(String[] args) throws Throwable, BrokenBarrierException
12     {
13         Thread t1 = new Thread(new Runnable()
14         {
15             @Override
16             public void run()
17             {
18                 try
19                 {
20                     c.await();
21                 }
22                 catch (InterruptedException e)
23                 {
24                     e.printStackTrace();
25                 }
26                 catch (BrokenBarrierException e)
27                 {
28                     e.printStackTrace();
29                 }
30                 System.out.println("1");
31
32             }
33         });
34         Thread t2 = new Thread(new Runnable()
35         {
36             @Override
37             public void run()
38             {
39                 try
40                 {
41                     c.await();
42                 }
43                 catch (InterruptedException e)
44                 {
45                     e.printStackTrace();
46                 }
47                 catch (BrokenBarrierException e)
48                 {
49                     e.printStackTrace();
50                 }
51                 System.out.println("2");
52             }
53         });
54
55         Thread t3 = new Thread(new Runnable()
56         {
57             @Override
58             public void run()
59             {
60                 try
61                 {
62                     c.await();
63                 }
64                 catch (InterruptedException e)
65                 {
66                     e.printStackTrace();
67                 }
68                 catch (BrokenBarrierException e)
69                 {
70                     e.printStackTrace();
71                 }
72                 System.out.println("3");
73
74             }
75         });
76         t1.start();
77         t2.start();
78         t3.start();
79         c.await();
80         System.out.println("4");
81     }
82 }

CyclicBarrier可以用于多线程计算数据,最后合并结果的场景;由于CyclicBarrier的计数器可以重置,所以可以使用它处理更为复杂的业务场景,而CountDownLatch计数器只能使用一次。

三、信号量Semaphore

无论是内部锁synchronized还是重入锁ReentrantLock,一次都只允许一个线程访问某一个资源,而信号量却可以指定多个线程同时访问某一资源;主要用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

 1 public class SemaphoreTest implements Runnable
 2 {
 3     final Semaphore s = new Semaphore(5);
 4     @Override
 5     public void run()
 6     {
 7         try
 8         {
 9             s.acquire();
10             Thread.sleep(1000);
11             System.out.println(Thread.currentThread().getName() + " is done");
12             s.release();
13         }
14         catch (InterruptedException e)
15         {
16             e.printStackTrace();
17         }
18     }
19 }

public class MainTest
{public static void main(String[] args){SemaphoreTest s = new SemaphoreTest();//创建一个可重用固定线程数的线程池,线程数量为20ExecutorService threadPool= Executors.newFixedThreadPool(20);for(int i=0;i<20;i++){threadPool.submit(s);}threadPool.shutdown();}
}

执行的结果是每五个线程为一组打印消息。

线程池里面有20个可重复使用的线程数量,但是信号量只有5个,也就是每次只能并发5个线程执行,其他线程阻塞。

信号量为5,可以认为线程池里有5把锁,每个线程调用acquire和release分别表示获取锁和释放锁,这样,通过信号量就可以调度多个线程的执行。

转载于:https://www.cnblogs.com/dongguacai/p/6023028.html

并发工具类:CountDownLatch、CyclicBarrier、Semaphore相关推荐

  1. JAVA并发:并发工具类CountDownLatch、CyclicBarrier、Semaphore使用及源码分析

    在 JUC 下包含了一些常用的同步工具类,今天就来详细介绍一下,CountDownLatch,CyclicBarrier,Semaphore 的使用方法以及它们之间的区别. 1 CountDownLa ...

  2. 多线程-并发工具类之CyclicBarrier详解

    文章目录 简介 例子 实现原理 小结 简介 从字面意思理解,CyclicBarrier是回环屏障的意思,它可以让一组线程全部达到一个状态后再全部同时执行.这里之所以叫作回环是因为当所有等待线程执行完毕 ...

  3. 死磕Java并发:J.U.C之并发工具类:CyclicBarrier

    作者:chenssy 来源:Java技术栈公众号 CyclicBarrier,一个同步辅助类,在API中是这么介绍的: 它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier ...

  4. JUC 常用 4 大并发工具类

    欢迎关注方志朋的博客,回复"666"获面试宝典 什么是JUC? JUC就是java.util.concurrent包,这个包俗称JUC,里面都是解决并发问题的一些东西 该包的位置位 ...

  5. Java并发工具类(闭锁CountDownLatch)

    并发工具类系列: Java并发工具类(闭锁CountDownLatch) Java并发工具类(栅栏CyclicBarrier) Java并发工具类(信号量Semaphore) 闭锁是一种同步工具类,可 ...

  6. 15.并发工具类(解析hashtable,ConcurrentHashMap1.7与1.8的区别以及Semaphore)

    3. 并发工具类 3.1 并发工具类-Hashtable Hashtable出现的原因:在集合类中HashMap是比较常用的集合对象,但是HashMap在多线程环境下可能会出现线程不安全的情况,为了保 ...

  7. 线程池,Volatile,原子性类AtomicInteger,乐观锁悲观锁,并发工具类Hashtable,ConcurrentHashMap类,Semaphore类

      目录 一.线程的状态 二.线程池 1.创建线程池的方式 1.1线程池-Executors默认线程池 1.2线程池-Executors创建指定上限的线程池 1.3线程池-ThreadPoolExec ...

  8. 死磕Java并发:J.U.C之并发工具类:Exchanger

    作者:chenssy 来源:Java技术驿站 前面三篇博客分别介绍了CyclicBarrier.CountDownLatch.Semaphore,现在介绍并发工具类中的最后一个Exchange.Exc ...

  9. 线程池、volatile、原子性、并发工具类

    目录 线程状态 线程池-基本原理 线程池 - Executors默认线程池 线程池 - ThreadPoolExecutor 线程池参数-拒绝策略 volatile 原子性 原子性 - AtomicI ...

  10. 第十章_多线程(2)_线程池原子性并发工具类

    目录 一.线程池 1 - 线程状态 2 - 线程池 3 - Executors线程池 二.Volatile 三.原子性 四.并发工具类 1 - 并发工具类-Hashtable 2 - 并发工具类-Co ...

最新文章

  1. android 提供服务,GitHub - FamliarMan/AndroidServiceProvider: 为模块化提供的一个服务发现库...
  2. 年度重磅!中国《营销自动化应用基准报告 2021》正式发布!
  3. 【日程发布】LiveVideoStackCon 音视频技术大会 2022 上海站
  4. [javascript]图解+注释版 Ext.extend()
  5. [Redis6]常用数据类型_List列表
  6. python列表统计每个元素出现次数_python 统计list中各个元素出现的次数的几种方法...
  7. python求两数之和的命令_python计算两个数的百分比方法
  8. 一道简单的编程题,不过您做对了吗?
  9. 【HDU5156】Harry and Christmas tree,两种离线的做法
  10. ic408服务器系统,威力铭408mt技术描述和配置.docx
  11. 2012年7月19日 解一元二次方程
  12. matlab 空集判定,在使用matlab 符号运算中的solve函数时,为啥计算的结果是空集?该怎么办?...
  13. c语言 发纸牌 实验报告,“21点”纸牌游戏实验报告
  14. ipad为什么显示itunes store无法连接服务器,ipad无法连接itunes store怎么办
  15. Lorenzo Von Matterhorn
  16. 光照传感器BH1750实验
  17. vue.js的快速入门使用
  18. hexo+yilia添加隐藏左边栏目按钮
  19. Android Studio Dolphin 稳定版正式发布
  20. 使用ubuntu20.04一个月后的感受

热门文章

  1. Java排序算法之——希尔排序
  2. 为什么有些产品不尽完美 但还是有大批用户愿意购买(转)
  3. 用SparseArray代替HashMap
  4. 单防区扩展模块怎么用_Zens推出模块化可扩展无线充电器 可为6台设备同时供电...
  5. 马拦过河卒(NOIP2002)
  6. 【Python-ML】SKlearn库性能指标ROC-AUC
  7. Tensorflow 模型加载及部分变量初始化
  8. java ajax无刷分页_asp.net+ajax+json来实现无刷新分页功能
  9. rockemq 发送延迟消息_RockeMQ通过代码监控消费者状态
  10. 热部署Devtools的简单使用