一、概述
Semaphore信号量,主要功能是限制能够运行的线程量,指定固定数量的许可,只有获取许可的线程可以运行,若没有许可获取则阻塞,直到获取许可。
二、使用示例
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
new Thread(new ThreadDemo(semaphore)).start();
new Thread(new ThreadDemo(semaphore)).start();
}
private static class ThreadDemo implements Runnable {
private Semaphore semaphore;
ThreadDemo(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("我获取了许可");
Thread.sleep(2000);
System.out.println("我要释放许可了");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行结果:
许可只有1个,只有当获取许可的线程释放之后,另一个线程才能执行
三、源码分析
Semaphore内部持有的同步器同样是通过AQS的共享锁实现,使用state表示许可的数量
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
/**
* 这里是非公平的实现方式,仅仅就比公平方式少了一步是否存在排队的判断
**/
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
// 如果剩余许可小于0就直接返回负数,否则通过CAS赋值,自旋直至成功或者许可耗尽
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
// 释放许可,CAS加自旋
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
// 减少许可数量
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
// 清空许可
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
Semaphore中同步器Sync继承AQS实现AQS中的共享模式tryReleaseShared(int releases)方法,同步器只实现了共享锁的释放,并未实现锁的获取tryAcquireShared(int arg)方法;由于公平和非公平获取许可的方式有所不同,所以该方法由对应子类实现。
1、构造函数
/**
* Creates a {@code Semaphore} with the given number of
* permits and nonfair fairness setting.
* 创建给定许可证数量信号量和非公平设置
* @param permits the initial number of permits available.
* This value may be negative, in which case releases
* must occur before any acquires will be granted.
* 初始许可证数量.
* 这个值允许为负数,在这种情况下必须在任何获取之前释放
*/
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
/**
* @param fair {@code true} 保证竞争先进先出
*/
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
Semaphore中公平和非公平是通过它的两个私有的静态内部类FairSync、NonFairSync实现,他们通过继承Sync并实现共享锁的获取tryAcquireShared(int arg)方法;
2、void acquire()获取许可
这个方法是从信号量获取一个许可,在获取到许可,或线程中断之前,当前线程阻塞;获取许可后立即返回并将许可数减一
/**
* 如果没有许可可用,则会休眠,直到发生以下两种情况
* 1、其他调用release方法释放许可,并且当前线程获取到许可
* 2、其他线程中断了当前线程
* 1)当前线程在进入这个方法时设置了中断标志位
* 2)等待许可时发生了中断,则抛出中断异常
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
这个方法是直接调用AQS的acquireSharedInterruptibly(int ard)方法; 方法实现原理请查看AQS源码
/**
* 首先检测是否中断.中断后抛出异常
* 尝试获取许可,成功退出;失败则进入AQS队列,直至成功获取或中断
*/
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 尝试获取锁,返回剩余共享锁的数量;小于0则加入同步队列,自旋
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared(arg)则会调用Semaphore中两个同步器的tryAcquireShared实现方法; 如果获取失败则加入队列等待唤醒;
非公平模式的实现
非公平实现都是首先查看是否有可获取的许可,如果有则获取成功,没有则进队列等待;利用此可以提高并发量
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
直接调用其父类Sync中非公平共享获取
final int nonfairTryAcquireShared(int acquires) {
// 自旋直到无许可或者状态位赋值成功
for (;;) {
int available = getState();
int remaining = available - acquires;
// 如果小于0则直接返回,否则利用CAS给AQS状态位赋值
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
通过自旋+CAS来一直尝试获取许可,直到获取成功或者没有许可,返回剩余的许可数
公平模式的实现
公平与非公平的区别在于始终按照AQS队列FIFO的顺序来的
protected int tryAcquireShared(int acquires) {
//自旋 CAS 实现线程安全
for (;;) {
// 判断是否有前置任务排队
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
如果等待队列不为空,则直接返回-1。 以上两种模式获取失败后都会调用doAcquireSharedInterruptibly(int arg);自旋等待获取锁
3、void release()
公平和非公平使用相同的释放 释放许可
public void release() {
sync.releaseShared(1);
}
调用AQS中的releaseShared(int arg)
public final boolean releaseShared(int arg) {
// 调用Sync实现的tryReleaseShared
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared(int arg)是父类Sync实现。自旋直到释放成功,许可数量+releases
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
如果释放许可成功,则调用AQS中的doReleaseShared()方法来唤醒AQS队列中等待的线程