Semaphore称为计数信号量,当线程来访问资源的时候,只有获得许可才可以成功来访问资源.
类继承关系
public class Semaphore implements java.io.Serializable {
}
Semaphore接口只实现了一个序列化接口.可以进行序列化.
内部类
abstract static class Sync extends AbstractQueuedSynchronizer {
}
static final class NonfairSync extends Sync {
}
tatic final class FairSync extends Sync {
}
类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQueuedSynchronizer抽象类。下面挨个分析实现类的细节.
内部类 - Sync类
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;
//许可小于零或者设置状态成功返回剩余许可.
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
// 共享模式下进行释放
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;
}
}
}
Sync类存在如下方法和作用如下.
内部类 - 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);
}
}
从源码tryAcquireShared方法可知,最终会调用父类的非公平nonfairTryAcquireShared方法获取许可.
内部类 - 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;
}
}
}
从tryAcquireShared方法的源码可知,它使用公平策略来获取资源,它会判断同步队列中是否存在其他的等待节点.依次获取许可.
类的属性
public class Semaphore implements java.io.Serializable {
// 版本号
private static final long serialVersionUID = -3222578661600680210L;
// 属性
private final Sync sync;
}
Semaphore自身只有两个属性,最重要的是sync属性,基于Semaphore对象的操作绝大多数都转移到了对sync的操作.
类的构造函数
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
该构造函数会创建具有给定的许可数和非公平的公平设置的Semaphore.
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
该构造函数会创建具有给定的许可数和给定的公平设置的Semaphore.
核心函数 - acquire函数
从信号量获取一个(多个)许可,在提供一个许可前一直将线程阻塞,或者线程被中断.源码如下.
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
该方法中将会调用Sync对象的acquireSharedInterruptibly(从AQS继承而来的方法)方法.acquireSharedInterruptibly找Semaphore实现,具体可以参考CountDownLatch.
核心函数 - release函数
方法释放一个(多个)许可,将其返回给信号量,源码如下.
public void release() {
sync.releaseShared(1);
}
该方法中将会调用Sync对象的releaseShared(从AQS继承而来的方法)方法.releaseShared找Semaphore实现,具体可以参考CountDownLatch.
Semaphore示例
class MyThread3 extends Thread {
private Semaphore semaphore;
public MyThread3(String name, Semaphore semaphore) {
super(name);
this.semaphore = semaphore;
}
@Override
public void run() {
int count = 3;
System.out.println(Thread.currentThread().getName() + " trying to acquire");
try {
semaphore.acquire(count);
System.out.println(Thread.currentThread().getName() + " acquire successfully");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(count);
System.out.println(Thread.currentThread().getName() + " release successfully");
}
}
}
public class SemaphoreDemo {
public final static int SEM_SIZE = 10;
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(SEM_SIZE);
MyThread3 t1 = new MyThread3("t1", semaphore);
MyThread3 t2 = new MyThread3("t2", semaphore);
t1.start();
t2.start();
int permits = 5;
System.out.println(Thread.currentThread().getName() + " trying to acquire");
try {
semaphore.acquire(permits);
System.out.println(Thread.currentThread().getName() + " acquire successfully");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println(Thread.currentThread().getName() + " release successfully");
}
}
}
执行结果.
生成一个信号量,信号量有10个许可,然后主线程和t1 t2依次获取.可能存在如下情况.
如上图所示,首先,main线程执行acquire操作,并且成功获得许可,之后t1线程执行acquire操作,成功获得许可,之后t2执行acquire操作,由于此时许可数量不够,t2线程将会阻塞,直到许可可用。之后t1线程释放许可,main线程释放许可,此时的许可数量可以满足t2线程的要求,所以,此时t2线程会成功获得许可运行,t2运行完成后释放许可.
main线程执行semaphore.acquire操作。主要的函数调用如下图所示
可以看到只是AQS的state变为了5,main线程并没有被阻塞,可以继续运行.
t1线程执行semaphore.acquire操作。主要的函数调用如下图所示
可以看到只是AQS的state变为了2,t1线程并没有被阻塞,可以继续运行 .
t2线程执行semaphore.acquire操作。主要的函数调用如下图所示
t2线程获取许可不会成功,之后会导致其被禁止运行,值得注意的是,AQS的state还是为2.
t1执行semaphore.release操作。主要的函数调用如下图所示
t2线程将会被unpark,并且AQS的state为5,t2获取cpu资源后可以继续运行
main线程执行semaphore.release操作。主要的函数调用如下图所示
t2线程还会被unpark,但是不会产生影响,此时,只要t2线程获得CPU资源就可以运行了。此时,AQS的state为10。
t2获取CPU资源,继续运行,此时t2需要恢复现场,回到parkAndCheckInterrupt函数中,也是在should继续运行。主要的函数调用如下图所示。
可以看到,Sync queue中只有一个结点,头节点与尾节点都指向该结点,在setHeadAndPropagate的函数中会设置头节点并且会unpark队列中的其他结点。
t2线程执行semaphore.release操作。主要的函数调用如下图所示
t2线程经过release后,此时信号量的许可又变为10个了,此时Sync queue中的结点还是没有变化 .