java并发编程系列原理篇--JDK中的通信工具类Semaphore
前言
java多线程之间进行通信时,JDK主要提供了以下几种通信工具类。主要有Semaphore、CountDownLatch、CyclicBarrier、exchanger、Phaser这几个通讯类。下面我们来详细介绍每个工具类的作用、原理及用法。
Semaphore介绍
Semaphore翻译过来是信号的意思。顾名思义,这个工具类提供的功能就是多个线程彼此“打信号”。而这个“信号”是一个int类型的数据,也可以看成是一种“资源”,用来限定线程访问该资源的数量。
它的构造函数有两个,一个参数的用来指定线程访问资源的数量;两个参数的一个用来指定线程访问资源的数量,一个用来指定是否为公平锁。关于公平锁非公平锁的概念请参照文章java并发编程系列之原理篇-synchronized与锁。构造函数代码如下:
// 默认情况下使用非公平
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
它的最主要的两个方法是acquire()和release()。acquire()方法会申请一个permit,而release方法会释放一个permit。当然,你也可以申请多个acquire(int permits)或者释放多个release(int permits)。每次acquire,permits就会减少一个或者多个。如果减少到了0,再有其他线程来acquire,那就要阻塞这个线程直到有其它线程release permit为止。
Semaphore的使用
Semaphore主要用来控制线程访问资源的数量的场景。举个例子,在并发条件下,我只想让3个线程来执行某一任务。请看示例代码:
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
new Thread(new MyThread(i,semaphore)).start();
}
}
static class MyThread implements Runnable{
private int id;//线程的ID号
private Semaphore semaphore;
public MyThread(int id, Semaphore semaphore){
this.id = id;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
//获取信号量permit许可
semaphore.acquire();
//接下来可以用来执行具体的线程任务
System.out.println(String.format("当前的线程是%d,还剩有%d个线程资源可以使用,有%d个线程处于等待中。",
id,semaphore.availablePermits(),semaphore.getQueueLength()));
Random random = new Random();
//随机睡眠时间,打乱释放顺序
Thread.sleep(random.nextInt(1000));
System.out.println(String.format("线程%d释放了资源",id));
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//任务结束,释放资源
semaphore.release();
}
}
}
}
输出结果:
当前的线程是1,还剩有1个线程资源可以使用,有0个线程处于等待中。
当前的线程是0,还剩有2个线程资源可以使用,有0个线程处于等待中。
当前的线程是2,还剩有0个线程资源可以使用,有0个线程处于等待中。
线程2释放了资源
当前的线程是3,还剩有0个线程资源可以使用,有6个线程处于等待中。
线程1释放了资源
当前的线程是4,还剩有0个线程资源可以使用,有5个线程处于等待中。
线程0释放了资源
当前的线程是5,还剩有0个线程资源可以使用,有4个线程处于等待中。
线程3释放了资源
当前的线程是6,还剩有0个线程资源可以使用,有3个线程处于等待中。
线程4释放了资源
当前的线程是7,还剩有0个线程资源可以使用,有2个线程处于等待中。
线程5释放了资源
当前的线程是8,还剩有0个线程资源可以使用,有1个线程处于等待中。
线程8释放了资源
当前的线程是9,还剩有0个线程资源可以使用,有0个线程处于等待中。
线程7释放了资源
线程6释放了资源
线程9释放了资源
从结果可以看出来,最初抢到这3个资源的线程是1,0,2,而其他线程进入了等待队列。之后每当有一个线程释放了该资源,才会有其他在等待队列的线程抢到资源。Semaphore默认的acquire方法是会让线程进入等待队列,且会抛出中断异常。但它还有一些方法可以忽略中断或不进入阻塞队列:
// 忽略中断
public void acquireUninterruptibly()
public void acquireUninterruptibly(int permits)
// 不进入等待队列,底层使用CAS
public boolean tryAcquire
public boolean tryAcquire(int permits)
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException
public boolean tryAcquire(long timeout, TimeUnit unit)
Semaphore的原理
Semaphore内部有一个继承了AQS的同步器Sync成员变量,重写了tryAcquireShared方法。在这个方法里,会去尝试获取资源。如果获取失败(想要的资源数量小于目前已有的资源数量),就会返回一个负数(代表尝试获取资源失败)。然后当前线程就会进入AQS的等待队列。具体的代码逻辑请查看JDK1.8中java.util.concurrent包下的Semaphore类。
参考链接
在这里很感谢能够有幸看到来自各个大厂大神们的开源项目深入浅出Java多线程,让我对多线程的知识有一个更深层次的了解。
java并发编程系列原理篇--JDK中的通信工具类Semaphore的更多相关文章
- java并发编程实战《二十一》无锁工具类
不安全的累加代码,如下 1 public class Test { 2 long count = 0; 3 void add10K() { 4 int idx = 0; 5 while(idx++ & ...
- java并发学习--第七章 JDK提供的线程工具类
一.ThreadLocal ThreadLocal类用于隔离多线程中使用的对象,为ThreadLocal类中传递的泛型就是要隔离的对象,简单的来说:如果我们在主线程创建了一个对象,并且需要给下面的多线 ...
- 原创】Java并发编程系列2:线程概念与基础操作
[原创]Java并发编程系列2:线程概念与基础操作 伟大的理想只有经过忘我的斗争和牺牲才能胜利实现. 本篇为[Dali王的技术博客]Java并发编程系列第二篇,讲讲有关线程的那些事儿.主要内容是如下这 ...
- Java并发编程系列-(9) JDK 8/9/10中的并发
9.1 CompletableFuture CompletableFuture是JDK 8中引入的工具类,实现了Future接口,对以往的FutureTask的功能进行了增强. 手动设置完成状态 Co ...
- Java并发编程系列-(8) JMM和底层实现原理
8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- Java并发编程系列-(4) 显式锁与AQS
4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关 ...
- 干货:Java并发编程系列之volatile(二)
接上一篇<Java并发编程系列之synchronized(一)>,这是第二篇,说的是关于并发编程的volatile元素. Java语言规范第三版中对volatile的定义如下:Java编程 ...
- Java并发编程系列-(5) Java并发容器
5 并发容器 5.1 Hashtable.HashMap.TreeMap.HashSet.LinkedHashMap 在介绍并发容器之前,先分析下普通的容器,以及相应的实现,方便后续的对比. Hash ...
随机推荐
- Docker 错误:network xxx id xxxx has active endpoints
问题描述:Docker Compose 部署的项目,使用docker-compose down 命令关闭时,提示错误: Removing network xxxl_default ERROR: net ...
- 2020由浅入深最强Java面试题
1.String,StringBuffer和StringBuilder有什么区别? String是字符串常量,不可变对象,每次对String修改都等同于生成了一个新的String象,然后将指针 ...
- Java使用Netty实现简单的RPC
造一个轮子,实现RPC调用 在写了一个Netty实现通信的简单例子后,萌发了自己实现RPC调用的想法,于是就开始进行了Netty-Rpc的工作,实现了一个简单的RPC调用工程. 如果也有兴趣动手造轮子 ...
- C#线程 线程进阶
第四部分:高级线程 非阻塞同步 前面我们说过,即使在分配或增加字段的简单情况下,也需要同步.尽管锁定始终可以满足此需求,但是竞争性锁定意味着线程必须阻塞,从而遭受上下文切换的开销和调度的延迟,这在高度 ...
- Matlab矩阵学习三 矩阵的运算
Matlab矩阵的运算 一.矩阵的加减 在matlab中,矩阵的加减和数的加减符号一样,都是"+"和”-“,不同的是两个进行运算的矩阵维度必须相同 二.数乘 三.乘法 矩阵乘法 ...
- 14 . Python3之MysSQL
数据库概念 数据库: 按照数据结构来组织.存储.管理数据的仓库` 诞生 计算机的发明是为了做科学计算的,而科学计算需要大量的输入和输出. 早期,可以使用打孔卡片的孔.灯泡的亮灭表示数据输入,输出. 后 ...
- 2018-ECCV-PNAS-Progressive Neural Architecture Search-论文阅读
PNAS 2018-ECCV-Progressive Neural Architecture Search Johns Hopkins University(霍普金斯大学) && Go ...
- [03]HTML基础之行内标签
1.<ruby>标签 显示东亚字符的发音(如中文,日文等),与<rp>,<rt>标签搭配. //<ruby>为单个发音字符的容器,<rp>为 ...
- 从按下url到显示页面
从按下url到渲染页面流程图 处理输入信息 检查用户输入 当用户在地址栏中输入一个查询关键字时,地址栏会判断输入的关键字是搜索内容,还是请求的 URL.如果是搜索内容,地址栏会使用浏览器默认的搜索引擎 ...
- ERROR: ...hbase.PleaseHoldException: Master is initializing
同学升级HBase后遇到这个问题,hbase shell,status就可以看到 ERROR: -hbase.PleaseHoldException: Master is initializing 解 ...