概要
一个计数信号量,维护了一组许可。acquire()
方法在许可可用前将阻塞,许可可用时获取,每个release()
方法添加一个许可,潜在地释放一个阻塞的获取线程。
信号量常用于约束访问一些(物理或逻辑)资源的线程数量。
信号量初始化为 1 可以用作互斥锁,更常见的是称为二进制信号量,因为它只有两个状态:有一个许可可用,或没有许可可用。当以这种方式使用时,二进制信号量有个属性,“锁”可以被其他线程而不是属主线程(信号量没有记录属主关系)释放。
公平性属性:公平策略,按FIFO顺序分配许可;非公平策略,允许闯入,即 acquire
调用线程获取许可,而不是已经在等待的线程。
一般地,信号量用于控制资源的访问应当初始化为公平的,来保证没有线程因为不能访问资源而饥饿。当把信号量用于其他同步类型的控制,非公平顺序的吞吐量优势压过公平的考量。
内存一致性效果:线程调用 “release” 之前的动作 happens-before 于成功调用 “acquire” 的其他线程的后续动作。
实现
由于信号量也只是一个计数器,它可以允许多个线程获取许可,如果没有许可可用则阻塞获取线程,这些都跟共享模式下的AQS非常吻合。
用AQS的状态来表示许可的数量。
// 非公平策略下获取许可
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;
}
}
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。