【JUC源码解析】Semaphore
简介
Semaphore(信号量),概念上讲,一个信号量持有一组许可(permits)。
概述
线程可调用它的acquire()方法获取一个许可,不成功则阻塞;调用release()方法来归还一个许可,前提是已经拿到过一个许可。然而,并没有什么实际的许可对象,Semaphore只是记录了一个数字,并根据这个数字管控线程。
应用
描述
有一组数据items,其实是26个大写的英文字母,有52个线程从中取数据(getItem),每个线程取一个数据,有一个线程不停地往里面放数据(putItem)直至取数据的52个线程都取到了数据。有一个信号量(Semaphore),要求同时只能有10个线程从中取数据(取数据之前会拿一个许可,共有10许可),除非,某个线程取得的数据,又被放数据的线程放进去了,模拟取数据线程操作结束,把许可证归还,其他取数据线程才能从中拿到许可,然后取数据。
代码
public class Pool {
private static final int MAX_AVAILABLE = 10;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
public String getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(String x) {
if (markAsUnused(x))
available.release();
}
private static void sleep(int sleep) {
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
protected boolean[] used = new boolean[MAX_AVAILABLE];
protected synchronized String getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null;
}
int len = 26;
protected String[] items = new String[len];
{
initial(0);
}
void initial(int sleep) {
for (int i = 0; i < len; i++) {
sleep(sleep);
items[i] = String.valueOf((char) (i + 65));
putItem(items[i]);
}
}
protected synchronized boolean markAsUnused(String item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
public static void main(String[] args) {
final Pool pool = new Pool();
final AtomicBoolean hasDone = new AtomicBoolean(false);
ExecutorService es = Executors.newFixedThreadPool(52);
new Thread(new Runnable() { // 计时器,每10毫秒记录一次
@Override
public void run() {
while (!hasDone.get()) {
System.out.print(".");
sleep(10);
}
}
}).start();
new Thread(new Runnable() { // 1秒后,每20毫秒添加一个元素
@Override
public void run() {
sleep(1000);
while (!hasDone.get()) {
pool.initial(20);
}
}
}).start();
final CyclicBarrier barrier = new CyclicBarrier(52, new Runnable() {
@Override
public void run() {
hasDone.set(true);
}
});
for (int i = 0; i < 52; i++) {
es.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread() + " << " + pool.getItem());
barrier.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
}
es.shutdown();
}
}
输出
.Thread[pool-1-thread-1,5,main] << A
Thread[pool-1-thread-2,5,main] << B
Thread[pool-1-thread-3,5,main] << C
Thread[pool-1-thread-4,5,main] << D
Thread[pool-1-thread-5,5,main] << E
Thread[pool-1-thread-6,5,main] << F
Thread[pool-1-thread-7,5,main] << G
Thread[pool-1-thread-8,5,main] << H
Thread[pool-1-thread-10,5,main] << I
Thread[pool-1-thread-9,5,main] << J
...............................................................................................Thread[pool-1-thread-12,5,main] << A
..Thread[pool-1-thread-11,5,main] << B
..Thread[pool-1-thread-13,5,main] << C
..Thread[pool-1-thread-14,5,main] << D
..Thread[pool-1-thread-15,5,main] << E
..Thread[pool-1-thread-16,5,main] << F
..Thread[pool-1-thread-17,5,main] << G
..Thread[pool-1-thread-18,5,main] << H
..Thread[pool-1-thread-19,5,main] << I
.Thread[pool-1-thread-20,5,main] << J
...............................Thread[pool-1-thread-21,5,main] << A
..Thread[pool-1-thread-22,5,main] << B
..Thread[pool-1-thread-24,5,main] << C
..Thread[pool-1-thread-23,5,main] << D
..Thread[pool-1-thread-25,5,main] << E
..Thread[pool-1-thread-26,5,main] << F
..Thread[pool-1-thread-27,5,main] << G
..Thread[pool-1-thread-28,5,main] << H
..Thread[pool-1-thread-29,5,main] << I
..Thread[pool-1-thread-30,5,main] << J
.................................Thread[pool-1-thread-31,5,main] << A
...Thread[pool-1-thread-32,5,main] << B
.Thread[pool-1-thread-33,5,main] << C
..Thread[pool-1-thread-34,5,main] << D
..Thread[pool-1-thread-35,5,main] << E
..Thread[pool-1-thread-36,5,main] << F
..Thread[pool-1-thread-37,5,main] << G
..Thread[pool-1-thread-38,5,main] << H
..Thread[pool-1-thread-39,5,main] << I
..Thread[pool-1-thread-42,5,main] << J
.................................Thread[pool-1-thread-41,5,main] << A
..Thread[pool-1-thread-40,5,main] << B
..Thread[pool-1-thread-43,5,main] << C
..Thread[pool-1-thread-44,5,main] << D
..Thread[pool-1-thread-52,5,main] << E
..Thread[pool-1-thread-45,5,main] << F
..Thread[pool-1-thread-51,5,main] << G
..Thread[pool-1-thread-50,5,main] << H
..Thread[pool-1-thread-49,5,main] << I
..Thread[pool-1-thread-48,5,main] << J
.................................Thread[pool-1-thread-47,5,main] << A
..Thread[pool-1-thread-46,5,main] << B
从上面输出可知,每10条记录,都要等一段时间,恰好体现了信号量管控的现象
源码分析
同步器对象
private final Sync sync; // 同步器对象
内部类
Sync
abstract static class Sync extends AbstractQueuedSynchronizer { // 内部类,继承自AQS
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) { // 构造方法
setState(permits); // 初始化许可的个数,对应state
}
final int getPermits() { // 获取当前许可的个数
return getState();
}
final int nonfairTryAcquireShared(int acquires) { // 获取锁,默认非公平锁
for (;;) {
int available = getState(); // 获取当前state,可用permits个数
int remaining = available - acquires; // 可用 - 获取 = 剩余
if (remaining < 0 || compareAndSetState(available, remaining)) // 如果remaining小于0,直接返回;否则CAS
// state为remaining,成功则返回,失败,则重新获取
return remaining;
}
}
protected final boolean tryReleaseShared(int releases) { // 释放锁
for (;;) {
int current = getState(); // 获取当前状态
int next = current + releases; // 当前值 + 释放值 = 下次可用值
if (next < current) // 溢出,说明releases的值小于0
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next)) // CAS state
// 为next,成功则返回;否则,重新获取并释放
return true;
}
}
final void reducePermits(int reductions) { // 减少许可个数
for (;;) {
int current = getState(); // 当前可用的个数
int next = current - reductions; // 剩余
if (next > current) // 说明reductions小于0
throw new Error("Permit count underflow");
if (compareAndSetState(current, next)) // CAS state为next,成功则返回;否则,重新获取并减少
return;
}
}
final int drainPermits() { // 排空许可,即情况所有的许可
for (;;) {
int current = getState(); // 获取当前状态
if (current == 0 || compareAndSetState(current, 0)) // CAS state
// 为0
return current;
}
}
}
NonfairSync
static final class NonfairSync extends Sync { // 非公平
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits); // 调用父类构造方法
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires); // 直接调用父类方法
}
}
FairSync
static final class FairSync extends Sync { // 公平
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits); // 调用父类构造方法
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors()) // 如果等待队列里有元素,直接返回失败,目的是让线程入队等待,保证公平性
return -1;
int available = getState(); // 后面同非公平逻辑
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
}
构造方法
public Semaphore(int permits) {
sync = new NonfairSync(permits); // 默认非公平
}
public Semaphore(int permits, boolean fair) { // 根据fair参数选择公平或非公平
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
其它方法
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1); // 获取许可,响应中断
}
public void acquireUninterruptibly() {
sync.acquireShared(1); // 不响应中断
}
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0; // false: < 0 true: > 0
}
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); // 支持超时
}
public void release() {
sync.releaseShared(1); // 释放许可
}
public void acquire(int permits) throws InterruptedException { // 获取多个
if (permits < 0)
throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits); // 响应中断
}
public void acquireUninterruptibly(int permits) { // 不响应中断
if (permits < 0)
throw new IllegalArgumentException();
sync.acquireShared(permits);
}
public boolean tryAcquire(int permits) { // false: < 0 true: > 0
if (permits < 0)
throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException {
if (permits < 0)
throw new IllegalArgumentException();
return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout)); // 支持超时
}
public void release(int permits) { // 释放多个
if (permits < 0)
throw new IllegalArgumentException();
sync.releaseShared(permits);
}
public int availablePermits() { // 可用的许可个数
return sync.getPermits();
}
public int drainPermits() { // 排空
return sync.drainPermits();
}
protected void reducePermits(int reduction) { // 减少
if (reduction < 0)
throw new IllegalArgumentException();
sync.reducePermits(reduction);
}
public boolean isFair() { // 是否是公平的
return sync instanceof FairSync;
}
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads(); // 队列里是否还有等待的线程
}
public final int getQueueLength() {
return sync.getQueueLength(); // 队列长度,等待线程的个数
}
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads(); // 获取在对列里等待的线程
}
public String toString() { // toString()方法
return super.toString() + "[Permits = " + sync.getPermits() + "]";
}
源码比较简单。
行文至此结束。
尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_semaphore.html
【JUC源码解析】Semaphore的更多相关文章
- 【JUC源码解析】ScheduledThreadPoolExecutor
简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...
- 【JUC源码解析】SynchronousQueue
简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...
- 【JUC源码解析】ForkJoinPool
简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...
- 【JUC源码解析】DelayQueue
简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...
- 【JUC源码解析】CyclicBarrier
简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...
- 【JUC源码解析】ConcurrentLinkedQueue
简介 ConcurrentLinkedQueue是一个基于链表结点的无界线程安全队列. 概述 队列顺序,为FIFO(first-in-first-out):队首元素,是当前排队时间最长的:队尾元素,当 ...
- 【JUC源码解析】Exchanger
简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...
- Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue
功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...
- Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer
功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...
随机推荐
- 5、Spring-Cloud-声明式调用 Feign(上)
5.1.写一个 Feign 害户端 新建项目: 依赖: <dependency> <groupId>org.springframework.boot</groupId&g ...
- pem文件转p12
p12->pem cer.p12: openssl pkcs12 -clcerts -nokeys -out cer.pem -in cer.p12 key.p12: openssl pkcs1 ...
- keepalived 的进程/usr/sbin/keepalived -D 只有2个
操作系统:openSUSE 11.3 (x86_64) /usr/sbin/keepalived -D 只有2条 日志:ls /var/log/messages* -lrth Can't init ...
- 404 Note Found队-现场编程
目录 组员职责分工 github 的提交日志截图 程序运行截图 程序运行环境 GUI界面 基础功能实现 运行视频 LCG算法 过滤(降权)算法 算法思路 红黑树 附加功能一 背景 实现 附加功能二(迭 ...
- BLE CC2541 串口BootLoader 即 SBL BootLoader 资料 收集
1.[CC254X_Bootloader]SBL(串口Bootloader)使用说明 2.CC2540协议栈高速串口通信解决(UART的DMA方式) 3.[BLE]CC2541之SBL 4.[BLE] ...
- 算法-求两个有序数组两两相加的值最小的K个数
我的思路是: 用队列, 从(0,0)開始入队,每次出队的时候,选(1,0) (0,1) 之间最小的入队,假设是相等的都入队,假设入过队的就不入了,把出队的k个不同的输出来就可以 我測试了几组数据都是 ...
- Strategy(策略)模式
1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Hard Cod ...
- Eclipse操作Servlet入门案例,创建类无法实现Servlet接口和继承HttpServlet类
问题描述: 从昨天下午到今天中午,这个问题被缠绕了良久.百度了很多,却都没有一言命中要害. 首先,还是基于对Web的理解. 第一:建立的是Dynamic Web Project: 第二:然后需要 ...
- ztz11的noip模拟赛T1:愤怒的XiaoX
链接: https://www.luogu.org/problemnew/show/U47231 思路: 这道题其实就是一道双Lazy线段树裸题 因为我们知道,当k一定时,取反偶数次最后k位等于不取反 ...
- C++笔记016:const 基础
原创笔记,转载请注明出处! 点击[关注],关注也是一种美德~ 笔记十六关于const关键字,在C语言和C++中const的表现是不同的. 先看一下const基础知识. 对const的初级理解:cons ...