Semaphore分析
摘要:
一、Semaphore介绍
Semaphore:信号量,可用来控制同时访问特定资源的线程数数量。它的本质上其实也是一个共享锁。Semaphore可以用于做流量控制,特别是公用资源有限的应用场景。例如:某个停车场车位只有10个,一开始停车场没有车辆所有车位全部空着,然后先后到来八辆车,停车场车位够,安排进去停车,然后又来三辆,这个时候由于只有两个停车位,所有只能停两辆,其余一辆必须在外面候着,直到停车场有空车位,当然以后每来一辆都需要在外面候着。当停车场有车开出去,里面有空位了,则安排一辆车进去。这个场景就是典型的信号量应用场景。
二、SemaphoreAPI使用介绍
2.1、构造函数:
-
Semaphore(int permits) :创建具有给定的许可数和非公平的公平设置的 Semaphore。
-
Semaphore(int permits, boolean fair) :创建具有给定的许可数和给定的公平设置的 Semaphore。
2.2、信号量获取:
1. Semaphore提供了acquire()方法来获取一个许可。
2.3、信号量释放:
1. Semaphore提供release()来释放许可。
2.4、其他方法:
1. intavailablePermits():返回此信号量中当前可用的许可证数。
2. intgetQueueLength():返回正在等待获取许可证的线程数。
3. booleanhasQueuedThreads():是否有线程正在等待获取许可证。
· 4. void reducePermits(int reduction):减少reduction个许可证,是个protected方法。
· 5. Collection getQueuedThreads():返回所有等待获取许可证的线程集合,是个protected方
法。
三.源代码分析
package com.cloudtravel.common.sourcecode;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* Semaphore是一个线程同步的辅助类,可以维护控制访问当前资源的线程并发个数,并提供了同步机制 ,
* 使用semaphore可以控制同时访问资源的个数.较多的如可以控制接口的并发个数
* 对比Mutex :
* Mutex一般用来串行化Critical section代码的访问,即最多同时允许一个线程访问 . 定义比较明显
* Semaphore: 控制资源允许的并发量 , 当其允许的并发量设置为1时,又称之为Binary Semaphore
* Binary Semaphore同Mutex对比:
* 作用:两者在所起到的作用上并没有什么区别 , 此时都是为了保证资源可以串行化访问,即都是保证同时只有一个线程访问资源 .
* 原理 : 两者在实现原理上有区别 .
* Semaphore: 为非对称模型,可以理解为,锁虽然只有一把,同时也只能有一把钥匙开着,但是不同的线程拥有的钥匙不同.
* semaphore可以理解为一个原子变量 , 信号量通过内部计数器,控制线程的并发量.且可以通过变更信号量[permit]进行灵活的调整
* Mutex: 为对称模型,即一把锁只有一把钥匙,线程之间通过这一把钥匙控制资源的访问 .也就是通过synchronized安全锁进行控制的.
* @author Administrator
*/
public class SemaphoreAnalyse implements java.io.Serializable {
private static final long serialVersionUID = -3222578661600680210L;
/**
* 通过AbstractQueuedSynchronizer子类实现的所有机制
*/
private final Sync sync;
/**
* Synchronization implementation for SemaphoreAnalyse. Uses AQS state
* to represent permits. Subclassed into fair and nonfair
* versions.
*/
/**
* 为semaphore定义的锁抽象类,继承自AQS,采用CAS算法,
* 借用AQS中的state控制是否允许访问 .
* sync为抽象类 . 主要定义了获取锁和释放锁的非公平锁方法 .
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
/** 调用AQS中的方法设置当前允许访问的并发数.permits : 创建semaphore实例的时候传进来 */
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;
}
}
}
/** 释放一定量的资源 */
@Override
protected final boolean tryReleaseShared(int releases) {
for (; ; ) {
int current = getState();
int next = current + releases;
if (next < current)
{
throw new Error("Maximum permit count exceeded");
}
if (compareAndSetState(current, next))
{
return true;
}
}
}
/** 减小信号量,同nonfairTryAcquireShared操作类似.只是只能减小,不能传入负值 */
final void reducePermits(int reductions) {
for (; ; ) {
int current = getState();
int next = current - reductions;
if (next > current) {
throw new Error("Permit count underflow");
}
if (compareAndSetState(current, next)) {
return;
}
}
}
/** 销毁信号量-将当前可使用信号量改为0.即不允许访问 */
final int drainPermits() {
for (; ; ) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
/** 非公平锁版本 */
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
/** 构造器 */
NonfairSync(int permits) {
super(permits);
}
/** 获取信号量资源 */
@Override
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
/**
* 公平锁版本
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
/** 获取资源信号量 当前state -1
* 对比NonFairSync多了hasQueuedPredecessors()的判断
* hasQueuedPredecessors()判断当前线程是否需要排队.并将当前线程加入到队列中
* 队列遵循FIFO[先进先出]的原则,若当前队列中无等待线程,则返回false表示不需要排队,
* 若存在等待线程,说明当前可用信号量已耗尽 , 需要排队,返回true.
* */
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;
}
}
}
/**
* 构造器-默认非公平锁版本
* @param permits
*/
public SemaphoreAnalyse(int permits) {
sync = new NonfairSync(permits);
}
/** 构造器,可自定义选择公平锁还是非公平锁 */
public SemaphoreAnalyse(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
/**
* 获取一个信号量, 当前state -1
* 其核心实现为AQS的doAcquireSharedInterruptibly()方法
* 若当前信号量资源耗尽,则会中断响应
* */
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
* 获取信号量,当前state -1
* 若当前信号量资源耗尽,则响应等待直到获取到资源 . 采用自旋锁
*/
public void acquireUninterruptibly() {
sync.acquireShared(1);
}
/**
* 获取一个信号量 . 当前state -1
* 若当前还有资源可用 . 返回true . 若无,返回false
* @return
*/
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
/**
* 获取一个信号量 . 当前state -1
* 若当前还有资源可用 . 返回true . 若无,返回false . 可自定义超时时间,超时中断响应
* @param timeout
* @param unit
* @return
* @throws InterruptedException
*/
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/** 释放一个资源 . 当前state + 1 */
public void release() {
sync.releaseShared(1);
}
/** 获取一定量的信号量资源 . 若当前信号量耗尽或者无法满足,则中断响应 */
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
/**
* 从该信号量分析获取给定数量的资源,直到所有许可证都可用为止。
* 获取给定数量的许可证(如果可用),并立即返回,将可用许可证数量减少给定数量。
* 如果没有足够的信号量可用,则当前线程出于线程调度目的将被禁用,并处于休眠状态,
* 直到其他线程调用此信号量分析的某个释放方法,当前线程下一个将被分配许可证,并且可用许可证的数量满足此请求。
* 如果当前线程在等待许可证时被中断,那么它将继续等待,并且它在队列中的位置不受影响。当线程确实从此方法返回时,将设置其中断状态。
* @param permits
*/
public void acquireUninterruptibly(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireShared(permits);
}
/**
* 仅当调用时所有许可证都可用时,从该信号量分析获取给定数量的许可证。
* 获取给定数量的许可证(如果可用),并立即返回,返回值为true,将可用许可证数量减少给定数量。
* 如果没有足够的许可证可用,则此方法将立即返回值false,并且可用许可证的数量不变。
* 即使此信号量分析已设置为使用公平排序策略,对tryAcquire的调用也将立即获得许可证(如果有),
* 无论其他线程当前是否正在等待。这种“讨价还价”行为在某些情况下是有用的,即使它破坏了公平性。
* 如果您想遵守公平性设置,那么使用几乎相等的tryAcquire(允许,0,TimeUnit.SECONDS)(它还检测中断)。
* @param permits
* @return
*/
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
/**
* 以共享定时模式获取信号量,超时中断响应
* @param permits
* @param timeout
* @param unit
* @return
* @throws InterruptedException
*/
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
}
/**
* 释放给定数量的许可证
* @param permits
*/
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
/**
* 返回当前可用的信号量
* @return
*/
public int availablePermits() {
return sync.getPermits();
}
/**
* 将当前可用信号量全部占用
* @return
*/
public int drainPermits() {
return sync.drainPermits();
}
/**
* 按指定的减少量缩小可用许可证的数量。此方法在使用信号量分析跟踪不可用资源的子类中非常有用。
* 此方法与acquire的不同之处在于,它不会发生线程阻塞
*/
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();
}
/**
* 获取队列中的所有等待线程 AQS内自带的方法
* @return the collection of threads
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
* toString
* @return a string identifying this SemaphoreAnalyse, as well as its state
*/
public String toString() {
return super.toString() + "[Permits = " + sync.getPermits() + "]";
}
}

浙公网安备 33010602011771号