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 ...
随机推荐
- 【MySQL】覆盖索引和回表
先来了解一下两大类索引 聚簇索引(也称聚集索引,主键索引等) 普通索引(也成非聚簇索引,二级索引等) 聚簇索引 如果表设置了主键,则主键就是聚簇索引 如果表没有主键,则会默认第一个NOT NULL,且 ...
- Excel中遇到的一些问题——持续更新
Q1:excel2007表格里的数字在表格关闭后再打开经常会变成日期格式,怎么解决? A1: 1)打开Excel,选中任意单元格,单击鼠标右键,选择设置单元格格式2)在数字自定义类型中,找到类似[$- ...
- 微信小程序制作个人简历
使用微信小程序云开发制作自己的个人简历,简单高效. 首先需要了解小程序的基本结构以及云开发模式,及一些css样式基础. 使用小程序云数据库.vant组件库实现简历内容. 最终效果如下: 首先申请个人微 ...
- OpenStack之Neutron模块
一:简介 一.概述 1. 传统的网络管理方式很大程度上依赖于管理员手工配置和维护各种网络硬件设备:而云环境下的网络已经变得非常复杂,特别是在多租户场景里,用户随时都可能需要创建.修改和删除网络 ...
- WEB常见攻击及防御
对于一个Web应用来说,可能会面临很多不同的攻击.下面的内容将介绍一些常见的攻击方法,以及面对这些攻击的防御手段. 一.跨站脚本攻击(XSS) 跨站脚本攻击的英文全称是Cross Site Scrip ...
- css3,transition,animation两种动画实现区别
我们为页面设置动画时,往往会用到transition还有animation以及transfrom属性或者用到js. 其实通常情况下,对于使用js我们更加倾向于使用css来设置动画. transfrom ...
- JavaScript中foreach、map、filter、find、every、some的用法
foreach:只是循环数组中的每一项,没有返回值 如: var arr = [2,3,3,4,5,6]; arr.foreach(function(item,index,array){ dosom ...
- 【Ubuntu】安装Ubuntu18.04.2LTS
环境:win10专业版.联想30D9主板 ubuntu:18.04.2LTS:Ubuntu镜像传送门:https://ubuntu.com/download/desktop 有两块硬盘,win10安装 ...
- 2019-ICLR-DARTS: Differentiable Architecture Search-论文阅读
DARTS 2019-ICLR-DARTS Differentiable Architecture Search Hanxiao Liu.Karen Simonyan.Yiming Yang GitH ...
- DevOps - 从渐进式交付说起(含实践 Demo)
作者:CODING - 王炜 1. 开篇 如果让你主导一款千万.甚至亿级用户产品的功能迭代,你会怎么做?你需要面对的挑战可能来自于: 商业战略的变化带来新的产品诉求,而产品的任何改动哪怕仅是界面调整, ...