JUC——线程同步辅助工具类(Semaphore,CountDownLatch,CyclicBarrier)
锁的机制从整体的运行转态来讲核心就是:阻塞,解除阻塞,但是如果仅仅是这点功能,那么JUC并不能称为一个优秀的线程开发框架,然而是因为在juc里面提供了大量方便的同步工具辅助类。
Semaphore信号量
Semaphore通常用于限制可以访问某些资源(物理or逻辑)的线程数目。
例如,大家排队去银行办理业务,但是只有两个银行窗口提供服务,来了10个人需要办理业务,所以这10个排队的人员需要依次使用这两个业务窗口来办理业务。
观察Semaphore类的基本定义:
public class Semaphore extends Object implements Serializable
Semaphore类中定义的方法有如下几个:
- 构造方法:
public Semaphore(int premits),设置服务的信号量;
- 构造方法:
public Semaphore(int premits,boolean fair) ,是否为公平锁;
- 等待执行:
public void acquireUninterruptibly(int permits)
- 设置的信号量上如果有阻塞的线程对象存在,那么将一直持续阻塞状态。
- 释放线程的阻塞状态:
public void release(int permits);
- 返回可用的资源个数:
public int availablePermits();
范例:实现银行排队业务办理
package so.strong.mall.concurrent;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; public class SemaphoreDemo {
public static void main(String[] args) {
final Semaphore semaphore = new Semaphore(2); //现在允许操作的资源一共有2个
final Random random = new Random(); //模拟每一个用户办理业务的时间
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() { //每一个线程就是一个要办理业务的人员
if (semaphore.availablePermits() > 0) { //现有空余窗口
System.out.println("[" + Thread.currentThread().getName() + "]进入银行,没有人办理业务");
} else { //没有空余位置
System.out.println("[" + Thread.currentThread().getName() + "]排队等候办理业务");
}
try {
semaphore.acquire(); //从信号量中获得操作许可
System.out.println("[" + Thread.currentThread().getName() + "]开始办理业务");
TimeUnit.SECONDS.sleep(random.nextInt(10)); //模拟办公延迟
System.out.println("[" + Thread.currentThread().getName() + "]结束业务办理");
semaphore.release(); //当前线程离开办公窗口
} catch (Exception e) {
e.printStackTrace();
}
}
}, "顾客-" + i).start();
}
}
}
[顾客-0]进入银行,没有人办理业务
[顾客-0]开始办理业务
[顾客-1]进入银行,没有人办理业务
[顾客-1]开始办理业务
[顾客-2]排队等候办理业务
[顾客-3]排队等候办理业务
[顾客-4]排队等候办理业务
[顾客-5]排队等候办理业务
[顾客-6]排队等候办理业务
[顾客-7]排队等候办理业务
[顾客-8]排队等候办理业务
[顾客-9]排队等候办理业务
[顾客-0]结束业务办理
[顾客-2]开始办理业务
[顾客-1]结束业务办理
[顾客-3]开始办理业务
[顾客-2]结束业务办理
[顾客-4]开始办理业务
[顾客-3]结束业务办理
[顾客-5]开始办理业务
[顾客-4]结束业务办理
[顾客-6]开始办理业务
[顾客-5]结束业务办理
[顾客-7]开始办理业务
[顾客-7]结束业务办理
[顾客-8]开始办理业务
[顾客-6]结束业务办理
[顾客-9]开始办理业务
[顾客-8]结束业务办理
这种信号量的处理在实际开发中有什么用呢?例如,现在对于数据库的连接一共有2个连接,那么可能有10个用户等待进行数据库操作,能够使用的连接个数为2个,这样10个用户就需要排队依次使用这两个连接来进行数据库操作。
CountDownLatch闭锁
CoundDownLatch描述的是一个计数的减少,下面首先观察一个程序的简单问题:
范例:编写一个简单的多线程开发
package so.strong.mall.concurrent;
public class CountDownDemo {
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("["+Thread.currentThread().getName()+"]线程应用执行完毕");
}
},"线程对象-"+i).start();
}
System.out.println("[***主线程***]所有的程序执行完毕");
}
}
[***主线程***]所有的程序执行完毕
[线程对象-1]线程应用执行完毕
[线程对象-0]线程应用执行完毕
现在可以发现,对于此时应该保证所有的线程执行完毕后在执行程序的输出计算,就好比:旅游团集合人员乘车离开。应该保证所有的线程都执行完毕了(指定个数的线程),这样的话就必须做一个计数处理。
CoundDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。
CoundDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
CoundDownLatch类之中的常用方法有如下几个:
- 构造方法:
public CountDownLatch(int count); //要设置一个等待的线程个数;
减少等待个数:
public void countDown();
等待countDownLatch为0:
public void await() throws InterruptedException;
范例:利用CountDownLatch解决之前的设计问题
package so.strong.mall.concurrent;
import java.util.concurrent.CountDownLatch; public class CountDownDemo {
public static void main(String[] args) throws Exception {
final CountDownLatch countDownLatch = new CountDownLatch(2); //2个线程全部执行完毕后可继续执行
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("[" + Thread.currentThread().getName() + "]线程应用执行完毕");
countDownLatch.countDown(); //减少等待的线程个数
}
}, "线程对象-" + i).start();
}
countDownLatch.await(); //等待计数的结束(个数为0)
System.out.println("[***主线程***]所有的程序执行完毕");
}
}
[线程对象-0]线程应用执行完毕
[线程对象-1]线程应用执行完毕
[***主线程***]所有的程序执行完毕
CyclicBarrier栅栏
CyclicBarrier和CountDownLatch是非常类似的,CyclicBarrier核心的概念是在于设置一个等待线程的数量边界,到达了此边界之后进行执行。
CyclicBarrier类是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点(Common Barrier Point)。
CyclicBarrier类是一种同步机制,它能够对处理一些算法的线程实现同。换句话讲,它就是一个所有线程必须等待的一个栅栏,直到所有线程都到达这里,然后所有线程才可以继续做其他事情。
通过调用CyclicBarrier对象的await()方法,两个线程可以实现互相等待。一旦N个线程在等待CyclicBarrier达成,所有线程将被释放掉去继续执行。
CyclicBarrier类的主要方法如下:
- 构造方法:
public CyclicBarrier(int parties);//设置等待的边界;
- 傻傻的等待其他线程:
public int await() throws InterruptedException, BrokenBarrierException;
- 等待其他线程:
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException;
- 重置等待的线程个数:
public void reset();
范例:观察CyclicBarrier进行等待处理
package so.strong.mall.concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit; public class CyclicBarrierDemo {
public static void main(String[] args) throws Exception{
final CyclicBarrier cyclicBarrier = new CyclicBarrier(2); //当凑够2个线程金进行触发
for (int i = 0; i < 3 ; i++) {
final int second = i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("["+Thread.currentThread().getName()+"]-等待开始");
try {
TimeUnit.SECONDS.sleep(second);
cyclicBarrier.await(); //等待处理
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("["+Thread.currentThread().getName()+"]-等待结束");
}
},"娱乐者-"+i).start();
}
}
}
[娱乐者-0]-等待开始
[娱乐者-1]-等待开始
[娱乐者-2]-等待开始
[娱乐者-1]-等待结束
[娱乐者-0]-等待结束
如果不想一直等待则可以设置超时时间,则超过了等待时间之后将会出现"TimeoutException"。
package so.strong.mall.concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit; public class CyclicBarrierDemo {
public static void main(String[] args) throws Exception{
final CyclicBarrier cyclicBarrier = new CyclicBarrier(2); //当凑够2个线程金进行触发
for (int i = 0; i < 3 ; i++) {
final int second = i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("["+Thread.currentThread().getName()+"]-等待开始");
try {
TimeUnit.SECONDS.sleep(second);
cyclicBarrier.await(6,TimeUnit.SECONDS); //等待处理
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("["+Thread.currentThread().getName()+"]-等待结束");
}
},"娱乐者-"+i).start();
}
}
}
[娱乐者-0]-等待开始
[娱乐者-1]-等待开始
[娱乐者-2]-等待开始
[娱乐者-1]-等待结束
[娱乐者-0]-等待结束
Disconnected from the target VM, address: '127.0.0.1:63717', transport: 'socket'
[娱乐者-2]-等待结束
java.util.concurrent.TimeoutException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
at so.strong.mall.concurrent.CyclicBarrierDemo$1.run(CyclicBarrierDemo.java:21)
at java.lang.Thread.run(Thread.java:745)
CyclicBarrier还有一个特点是可以进行重置处理
范例:重置处理
package so.strong.mall.concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit; public class CyclicBarrierResetDemo {
public static void main(String[] args) throws Exception {
final CyclicBarrier cb = new CyclicBarrier(2); //当凑够2个线程就进行触发
for (int i = 0; i < 3; i++) {
final int second = i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("[" + Thread.currentThread().getName() + "]-等待开始");
try {
if (second == 2) {
cb.reset(); //重置
System.out.println("[重置处理****]" + Thread.currentThread().getName());
} else {
TimeUnit.SECONDS.sleep(second);
cb.await(6,TimeUnit.SECONDS);//等待处理
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("[" + Thread.currentThread().getName() + "]-等待结束");
}
}, "娱乐者-" + i).start();
}
}
}
[娱乐者-0]-等待开始
[娱乐者-1]-等待开始
[娱乐者-2]-等待开始
[重置处理****]娱乐者-2
[娱乐者-2]-等待结束
[娱乐者-1]-等待结束
[娱乐者-0]-等待结束
CountDownLatch与CyclicBarrier的区别:
- CountDownLatch最大的特征是进行一个数据减法的操作等待,所有的统计操作一旦开始之中就必须执行countDown()方法,如果等待个数不是0,就被一只等待,并且无法重置。
- CyclicBarrier设置一个等待的临界点,并且可以有多个等待线程出现,只要满足了临界点就触发了线程的执行代码后将重新开始进行计数处理操作,也可以直接利用reset()方法执行重置操作。
JUC——线程同步辅助工具类(Semaphore,CountDownLatch,CyclicBarrier)的更多相关文章
- JUC——线程同步辅助工具类(Exchanger,CompletableFuture)
Exchanger交换空间 如果现在有两个线程,一个线程负责生产数据,另外一个线程负责消费数据,那么这个两个线程之间一定会存在一个公共的区域,那么这个区域的实现在JUC包之中称为Exchanger. ...
- 并发工具类:CountDownLatch、CyclicBarrier、Semaphore
在多线程的场景下,有些并发流程需要人为来控制,在JDK的并发包里提供了几个并发工具类:CountDownLatch.CyclicBarrier.Semaphore. 一.CountDownLatch ...
- 30行自己写并发工具类(Semaphore, CyclicBarrier, CountDownLatch)是什么体验?
30行自己写并发工具类(Semaphore, CyclicBarrier, CountDownLatch)是什么体验? 前言 在本篇文章当中首先给大家介绍三个工具Semaphore, CyclicBa ...
- 【同步工具类】CountDownLatch闭锁任务同步
[同步工具类]CountDownLatch闭锁任务同步 转载:https://www.cnblogs.com/yangchongxing/p/9214284.html 打过dota的同学都知道,多人一 ...
- Java并发编程系列-(2) 线程的并发工具类
2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...
- Java线程的并发工具类
Java线程的并发工具类. 一.fork/join 1. Fork-Join原理 在必要的情况下,将一个大任务,拆分(fork)成若干个小任务,然后再将一个个小任务的结果进行汇总(join). 适用场 ...
- 【Java并发工具类】CountDownLatch和CyclicBarrier
前言 下面介绍协调让多线程步调一致的两个工具类:CountDownLatch和CyclicBarrier. CountDownLatch和CyclicBarrier的用途介绍 CountDownLat ...
- 类型转换辅助工具类TypeCaseHelper
package org.sakaiproject.util; import java.math.BigDecimal; import java.sql.Date; import java.sql.Ti ...
- 多线程状态与优先级、线程同步与Monitor类、死锁
一.线程状态 二.线程优先级 三.初步尝试多线程 class Program { static void Main(string[] args) { while (true) { MessagePri ...
随机推荐
- Python中网络编程对socket accept函数的理解
在服务器端,socket()返回的套接字用于监听(listen)和接受(accept),这个套接字不能用于与客户端之间发送和接收数据. accept()接受一个客户端的连接请求,并返回一个新的套接字, ...
- BZOJ1444:[JSOI2009]有趣的游戏(AC自动机,矩阵乘法)
Description Input 注意 是0<=P, n , l, m≤ 10. Output Sample Input input 1 3 2 2 1 2 1 2 AB BA AA inpu ...
- 简单的Windows 服务的安装和卸载
步骤: 1.运行--〉cmd:打开cmd命令框 2.在命令行里定位到InstallUtil.exe所在的位置 InstallUtil.exe 默认的安装位置是在C:/Windows/Microsoft ...
- [AHOI2005]矿藏编码
嘟嘟嘟 这道题题面我是看了小半天才懂(太菜了),然后就发现好水啊. 只要维护一个栈,存的是t,代表当前的正方形是2t * 2t的,然后从头开始扫序列,如果遇到2,就把栈顶元素取出来,然后放进去四个t ...
- Jenkins启动和停止服务
1.怎么启动Jenkins? step1:进入到Jenkins的war包所在的目录. 如果是win7及以上版本,直接打开Jenkins的war包所在的目录,在地址栏敲cmd,回车. 上述结果和进入cm ...
- django admin自定义
django admin自定义 class CustomerAdmin(admin.ModelAdmin): list_display = ('id','name','qq','consultant' ...
- 添加打印功能 iOS
app直接调取系统的打印功能 https://blog.csdn.net/wsyx768/article/details/79098885
- python 编码规范起源:PEP8 编码规范中文版
PEP: 8 标题: Python代码的样式指南 版: c451868df657 最后修改: 2016-06-08 10:43:53 -0400(2016年6月8日星期三) 作者: Guido van ...
- bat取时间间隔
@echo off echo 现在时间是%time:~,%点%time:~,%分%time:~,%秒 ,%%time:~,%%time:~,% pause echo 现在时间是%time:~,%点%t ...
- Spring Cloud 微服务项目实现总架构一
Spring Cloud 服务是一种分布式服务,包括配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,主节点选举, 分布式session, 集群状态等公共组件. 一 注册机 ...