前言

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的更多相关文章

  1. java并发编程实战《二十一》无锁工具类

    不安全的累加代码,如下 1 public class Test { 2 long count = 0; 3 void add10K() { 4 int idx = 0; 5 while(idx++ & ...

  2. java并发学习--第七章 JDK提供的线程工具类

    一.ThreadLocal ThreadLocal类用于隔离多线程中使用的对象,为ThreadLocal类中传递的泛型就是要隔离的对象,简单的来说:如果我们在主线程创建了一个对象,并且需要给下面的多线 ...

  3. 原创】Java并发编程系列2:线程概念与基础操作

    [原创]Java并发编程系列2:线程概念与基础操作 伟大的理想只有经过忘我的斗争和牺牲才能胜利实现. 本篇为[Dali王的技术博客]Java并发编程系列第二篇,讲讲有关线程的那些事儿.主要内容是如下这 ...

  4. Java并发编程系列-(9) JDK 8/9/10中的并发

    9.1 CompletableFuture CompletableFuture是JDK 8中引入的工具类,实现了Future接口,对以往的FutureTask的功能进行了增强. 手动设置完成状态 Co ...

  5. Java并发编程系列-(8) JMM和底层实现原理

    8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...

  6. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  7. Java并发编程系列-(4) 显式锁与AQS

    4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关 ...

  8. 干货:Java并发编程系列之volatile(二)

    接上一篇<Java并发编程系列之synchronized(一)>,这是第二篇,说的是关于并发编程的volatile元素. Java语言规范第三版中对volatile的定义如下:Java编程 ...

  9. Java并发编程系列-(5) Java并发容器

    5 并发容器 5.1 Hashtable.HashMap.TreeMap.HashSet.LinkedHashMap 在介绍并发容器之前,先分析下普通的容器,以及相应的实现,方便后续的对比. Hash ...

随机推荐

  1. 17-4 delete-truncate语句-练习

    --删除数据语句: --delete from 表名 where ... --delete语句如果不加where条件,表示将表中所有的数据都删除,加where条件后,会按照where条件进行删除. - ...

  2. docker 日志查看与清洗

    Linux下查看磁盘与目录的容量——df.du df:列出文件系统的整体磁盘使用量: du:评估文件系统的磁盘使用量(常用于评估目录所占容量) df参数: -a:列出所有的文件系统,包括系统特有的/p ...

  3. vue中template的三种写法

    第一种(使用模板字符串)早期字符串拼接年代 <div id="app"></div> new Vue({ el: "#app", tem ...

  4. 这次终于可以愉快的进行 appium 自动化测试了

    appium 是进行 app 自动化测试非常成熟的一套框架.但是因为 appium 设计到的安装内容比较多,很多同学入门都跪在了环境安装的部分.本篇讲述 appium 安卓环境的搭建,希望让更多童鞋轻 ...

  5. [Objective-C] 011_数据持久化_NSKeyedArchiver

    在日常开发中对于NSString.NSDictionary.NSArray.NSData.NSNumber这些基本类的数据持久化,可以用属性列表的方法持久化到.plist 文件中.但是一些我们自定义的 ...

  6. Rocket - tilelink - Bits vs. UInt

    https://mp.weixin.qq.com/s/lzDmIHkUph3b1Fxgx66ySg   分析移位/取反操作在Intellij中提示错误的问题.   1. 问题   用到移位/取反的地方 ...

  7. Java并发编程 (二) 并发基础

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.CPU多级缓存-缓存一致性 1.CPU多级缓存 ​ 上图展示的是CPU高级缓存的配置,数据的读取和存 ...

  8. Java实现 蓝桥杯VIP 算法训练 集合运算

    问题描述 给出两个整数集合A.B,求出他们的交集.并集以及B在A中的余集. 输入格式 第一行为一个整数n,表示集合A中的元素个数. 第二行有n个互不相同的用空格隔开的整数,表示集合A中的元素. 第三行 ...

  9. Java实现 LeetCode 113 路径总和 II

    113. 路径总和 II 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标和 sum = ...

  10. Java实现冗余路径Redundant Paths

    Description In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numb ...