并发工具类:CountDownLatch、CyclicBarrier、Semaphore
在多线程的场景下,有些并发流程需要人为来控制,在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相关推荐
- JAVA并发:并发工具类CountDownLatch、CyclicBarrier、Semaphore使用及源码分析
在 JUC 下包含了一些常用的同步工具类,今天就来详细介绍一下,CountDownLatch,CyclicBarrier,Semaphore 的使用方法以及它们之间的区别. 1 CountDownLa ...
- 多线程-并发工具类之CyclicBarrier详解
文章目录 简介 例子 实现原理 小结 简介 从字面意思理解,CyclicBarrier是回环屏障的意思,它可以让一组线程全部达到一个状态后再全部同时执行.这里之所以叫作回环是因为当所有等待线程执行完毕 ...
- 死磕Java并发:J.U.C之并发工具类:CyclicBarrier
作者:chenssy 来源:Java技术栈公众号 CyclicBarrier,一个同步辅助类,在API中是这么介绍的: 它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier ...
- JUC 常用 4 大并发工具类
欢迎关注方志朋的博客,回复"666"获面试宝典 什么是JUC? JUC就是java.util.concurrent包,这个包俗称JUC,里面都是解决并发问题的一些东西 该包的位置位 ...
- Java并发工具类(闭锁CountDownLatch)
并发工具类系列: Java并发工具类(闭锁CountDownLatch) Java并发工具类(栅栏CyclicBarrier) Java并发工具类(信号量Semaphore) 闭锁是一种同步工具类,可 ...
- 15.并发工具类(解析hashtable,ConcurrentHashMap1.7与1.8的区别以及Semaphore)
3. 并发工具类 3.1 并发工具类-Hashtable Hashtable出现的原因:在集合类中HashMap是比较常用的集合对象,但是HashMap在多线程环境下可能会出现线程不安全的情况,为了保 ...
- 线程池,Volatile,原子性类AtomicInteger,乐观锁悲观锁,并发工具类Hashtable,ConcurrentHashMap类,Semaphore类
目录 一.线程的状态 二.线程池 1.创建线程池的方式 1.1线程池-Executors默认线程池 1.2线程池-Executors创建指定上限的线程池 1.3线程池-ThreadPoolExec ...
- 死磕Java并发:J.U.C之并发工具类:Exchanger
作者:chenssy 来源:Java技术驿站 前面三篇博客分别介绍了CyclicBarrier.CountDownLatch.Semaphore,现在介绍并发工具类中的最后一个Exchange.Exc ...
- 线程池、volatile、原子性、并发工具类
目录 线程状态 线程池-基本原理 线程池 - Executors默认线程池 线程池 - ThreadPoolExecutor 线程池参数-拒绝策略 volatile 原子性 原子性 - AtomicI ...
- 第十章_多线程(2)_线程池原子性并发工具类
目录 一.线程池 1 - 线程状态 2 - 线程池 3 - Executors线程池 二.Volatile 三.原子性 四.并发工具类 1 - 并发工具类-Hashtable 2 - 并发工具类-Co ...
最新文章
- android 提供服务,GitHub - FamliarMan/AndroidServiceProvider: 为模块化提供的一个服务发现库...
- 年度重磅!中国《营销自动化应用基准报告 2021》正式发布!
- 【日程发布】LiveVideoStackCon 音视频技术大会 2022 上海站
- [javascript]图解+注释版 Ext.extend()
- [Redis6]常用数据类型_List列表
- python列表统计每个元素出现次数_python 统计list中各个元素出现的次数的几种方法...
- python求两数之和的命令_python计算两个数的百分比方法
- 一道简单的编程题,不过您做对了吗?
- 【HDU5156】Harry and Christmas tree,两种离线的做法
- ic408服务器系统,威力铭408mt技术描述和配置.docx
- 2012年7月19日 解一元二次方程
- matlab 空集判定,在使用matlab 符号运算中的solve函数时,为啥计算的结果是空集?该怎么办?...
- c语言 发纸牌 实验报告,“21点”纸牌游戏实验报告
- ipad为什么显示itunes store无法连接服务器,ipad无法连接itunes store怎么办
- Lorenzo Von Matterhorn
- 光照传感器BH1750实验
- vue.js的快速入门使用
- hexo+yilia添加隐藏左边栏目按钮
- Android Studio Dolphin 稳定版正式发布
- 使用ubuntu20.04一个月后的感受
热门文章
- Java排序算法之——希尔排序
- 为什么有些产品不尽完美 但还是有大批用户愿意购买(转)
- 用SparseArray代替HashMap
- 单防区扩展模块怎么用_Zens推出模块化可扩展无线充电器 可为6台设备同时供电...
- 马拦过河卒(NOIP2002)
- 【Python-ML】SKlearn库性能指标ROC-AUC
- Tensorflow 模型加载及部分变量初始化
- java ajax无刷分页_asp.net+ajax+json来实现无刷新分页功能
- rockemq 发送延迟消息_RockeMQ通过代码监控消费者状态
- 热部署Devtools的简单使用