java多线程系列:Semaphore和Exchanger
本篇文章将介绍Semaphore和Exchanger这两个并发工具类。
Semaphore
信号量(英语:Semaphore)又称为信号标,是一个同步对象,用于保持在0至指定最大值之间的一个计数值。当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一;当线程完成一次对semaphore对象的释放(release)时,计数值加一。当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态。semaphore对象的计数值大于0,为signaled状态;计数值等于0,为nonsignaled状态.
semaphore对象适用于控制一个仅支持有限个用户的共享资源,是一种不需要使用忙碌等待(busy waiting)的方法。 ----取自维基百科
Semaphore思想在分布式中也有应用,分布式限流就是典型的案例。现在举个小例子来使用Semaphore
案例
在等公交时,遇到人多的时候经常需要排队或者挤进去。

解决方案
利用Semaphore初始化5个许可,每次只能有5个玩家进入,当有玩家退出时,其他玩家才能进入。
先介绍下Semaphore的构造函数和一些方法吧。
Semaphore构造函数
public Semaphore(int permits);
public Semaphore(int permits, boolean fair);
第一个参数permits表示初始化的许可数量,第二个参数表示是否是公平的。
使用Semaphore(int permits)构造函数时,默认使用非公平的
Semaphore常用方法
public void acquire();
public void release();
acquire方法取得许可,release方法表示释放许可。
注:如果多次调用release方法,会增加许可。例如,初始化许可为0,这时调用了两个release方法,Semaphore的许可便会变成2
这两个是最常用的方法,其他的还有acquire相关的方法tryAcquire和acquireUninterruptibly这里就不介绍了。
代码
玩家类
定义一个实现Runnable接口的玩家类
public class Player implements Runnable{
private String playerName;
private Semaphore semaphore;
public Player(String playerName, Semaphore semaphore) {
this.playerName = playerName;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(playerName+"进入,时间:"+LocalTime.now());
Thread.sleep((long) (3000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(playerName+"退出");
semaphore.release();
}
}
}
通过构造函数Player传入玩家名称和Semaphore对象,run方法中先调用acquire方法取得许可,然后睡眠随机时间,最后在finally中调用release方法释放许可。
测试类
先来使用非公平的看看效果,使用非公平的就好比平时的挤公交,谁先在车门口谁先进。如下图(来源于网络)

现在来看看测试代码
public static void main(String[] args) throws IOException {
Semaphore semaphore = new Semaphore(5);
//Semaphore semaphore = new Semaphore(5,true);
ExecutorService service = Executors.newCachedThreadPool();
//模拟100个玩家排队
for (int i = 0; i < 100; i++) {
service.submit(new Player("玩家"+i,semaphore));
}
//关闭线程池
service.shutdown();
//判断线程池是否中断,没有则循环查看当前排队总人数
while (!service.isTerminated()){
System.out.println("当前排队总人数:"+semaphore.getQueueLength());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如果要切换成公平方式只需将上面初始化Semaphore改为下面的代码即可
Semaphore semaphore = new Semaphore(5,true);
Exchanger
Exchanger主要用于线程间的数据交换。 它提供了一个同步点,在这个同步点,两个线程可以交换数据。
这里写了个两个线程互相交换数据的简单例子,下面ExchangerRunnable在run方法中调用exchange方法将自己的数据传过去。
public class ExchangerRunnable implements Runnable {
private Object data;
private String name;
private Exchanger exchanger;
public ExchangerRunnable(String name, Exchanger exchanger, Object data) {
this.exchanger = exchanger;
this.name = name;
this.data = data;
}
public void run() {
try {
Object previous = this.data;
this.data = this.exchanger.exchange(previous);
System.out.println("名称:" + name + " 之前数据:" + previous + " ,交换之后数据:" + this.data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
接下来看看测试代码
public class Case {
private static final Exchanger exchanger = new Exchanger();
private static ExecutorService service = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
service.submit(new ExchangerRunnable("1", exchanger, "A"));
service.submit(new ExchangerRunnable("2", exchanger, "B"));
service.shutdown();
}
}
定义了只包含两个线程的线程池,然后创建提交两个ExchangerRunnable的类
- 线程名称为1的原始数据时A
- 线程名称为2的原始数据时B
运行测试代码,会得到如下结果
名称:2 之前数据:B ,交换之后数据:A
名称:1 之前数据:A ,交换之后数据:B
欢迎fork、Star、Issue等,谢谢
java多线程系列:Semaphore和Exchanger的更多相关文章
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例
概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”01之 框架
本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...
- Java多线程系列目录(共43篇)
最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...
- Java多线程系列--“JUC锁”07之 LockSupport
概述 本章介绍JUC(java.util.concurrent)包中的LockSupport.内容包括:LockSupport介绍LockSupport函数列表LockSupport参考代码(基于JD ...
- Java多线程系列--“JUC锁”08之 共享锁和ReentrantReadWriteLock
概要 Java的JUC(java.util.concurrent)包中的锁包括"独占锁"和"共享锁".在“Java多线程系列--“JUC锁”02之 互斥锁Ree ...
- Java多线程系列
一.参考文献 1.:Java多线程系列目录 (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式 03. ...
- java多线程系列 目录
Java多线程系列1 线程创建以及状态切换 Java多线程系列2 线程常见方法介绍 Java多线程系列3 synchronized 关键词 Java多线程系列4 线程交互(wait和 ...
- Java多线程系列——从菜鸟到入门
持续更新系列. 参考自Java多线程系列目录(共43篇).<Java并发编程实战>.<实战Java高并发程序设计>.<Java并发编程的艺术>. 基础 Java多线 ...
- Java多线程系列目录(转)
转载方便自己学习,转自:Java多线程系列目录(共43篇) http://www.cnblogs.com/skywang12345/p/java_threads_category.html 最近,在研 ...
随机推荐
- 加密第四节_IPSec基本理论
加密第四节_IPSec基本理论 本节内容 IPSec简介 IPSec两种工作模式 判断隧道模式和传输模式 IPSec两种模型 IPSec两个数据库 IPSec基本理论 IPSec简介 提供了网络层的安 ...
- 软件架构设计 ADMEMS方法体系
ADMEMS是Architecture Design Method has been Extended to Method System的简称,是由CSAI顾问团架构设计专家组于2009年11月在第六 ...
- python面向对象-我的理解
参考:博客 Vamei .廖雪峰 面向对象概念 面向对象完全可以按照自然界生物分类法来理解. 当然,它不会有自然界那么复杂. 因为我专业的关系,因此个人觉得微生物来举例很容易理解. 所有的微生物都具有 ...
- 三层自动生成 完整源代码(for oracle)
using Oracle.DataAccess.Client; using System; using System.Collections.Generic; using System.Compone ...
- Java中throw和throws的区别【转】
系统自动抛出的异常所有系统定义的编译和运行异常都可以由系统自动抛出,称为标准异常,并且 Java 强烈地要求应用程序进行完整的异常处理,给用户友好的提示,或者修正后使程序继续执行. 语句抛出的异常 用 ...
- SQL字符串拼接
不同的数据库,相应的字符串拼接方式不同,通过对比加深一下记忆. 一.MySQL字符串拼接 1.CONCAT函数 语法格式:CONCAT(char c1, char c2, ..., char cn) ...
- PostgreSQL 备份和恢复
备份和恢复有三种不同的基本方法来备份PostgreSQL数据SQL转储文件系统级备份File system level backup连续归档 1. SQL转储 pg_dump dbname > ...
- MySQL 双主+keepalived 详细文档 M-M+keepalived
1. 操作系统,系统环境,目结结构,用户,权限,日志路径,脚本2. 配置规范化 1.1 操作系统准备 操作系统 Kylin Linux release 3.3.1707 (Core)数据库版本 mys ...
- 侯捷STL学习(六)--深入list && Iterator traits
第十三,四节 深度探索list(上,下) list Gnu2.9源代码实现 注意node代码和图示的位置 实现前闭后开,增加一个空白节点 用的分配器alloc Iterator智能指针,需要知道结点n ...
- ruby中nil?, empty? and blank?
In Ruby, you check with nil? if an object is nil: article = nil article.nil? # => true empty? che ...