一、简介:
Semaphore(信号量)是用来控制同
时访问
特定
资
源的
线
程数量,它通
过协调
各个
线
程,以
保
证
合理的使用公共
资源。实现其实就是一个共享锁,是基于AQS实现的,通过state变量来实现共享。通过调用acquire方法,对state值减去一,当调用release的时候,对state值加一。当state变量小于0的时候,在AQS队列中阻塞等待
二、使用场景:
当我们需要对某个任务限制资源使用时,比如我们这个系统 有多个接口,其中一个接口不重要其他接口特别重要,这时候就可以通过信号量限制这个接口可使用的线程数量防止再一些特殊情况这个接口使用超量的线程资源从而影响到重要任务的执行
三、构造方法:
Semaphore可以实现公平和非公平
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
其中NonfairSync和FairSync都是继承自Sync,而Sync继承于AQS所以Semaphore就是通过AQS实现的
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits);
}
}
从这里可以看出来调用了AQS的setState方法,读过前面的文章应该明白AQS的核心就是内部维护着一个volatile修饰的同步状态值state。所以说当我们new Semaphore(10)时候,实际上是在AQS的框架中初始化了一个同步状态为10的值。
四、acquire方法:
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
通过源代码我们发现此处semphore处理获取锁的业务逻辑是:
- 获取同步状态值
- 每个线程进来就减去请求的值,此处请求的值是1.然后用可用同步状态值减去请求的值得到同步状态剩余的值。
- 如果请求的值大于可用的值或者CAS操作把可用值改为剩余可用的值那么就返回剩下可用的值。
五、release()释放锁
Semaphore semaphore=new Semaphore(10);
semaphore.release();
public void release() {
sync.releaseShared(1);
}
此处sync调用了AQS中的方法releaseShared,在这个方法中如果释放成功那么就调用doReleaseShared方法,此方法在前面AQS共享模式文章中已经讲解过,此处不在详细讲解。它主要作用就是释放队列中的节点。
六、整体流程
附上网图
七、实例代码
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
Semaphore semaphore = new Semaphore(5);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 10, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
for (int i = 0; i < 20; i++) {
threadPoolExecutor.execute(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "开始执行");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "执行成功############");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
输出:
可以看出最多只有五个线程在执行