难堪的一次面试: 信号量Semaphore了解过吗?没有😖
线程同步器CycliBarrier你都不会吗,打击到了我。。
CycliBarrier、CountDownLatch都是减计数器,而Semaphore都是加计数器
下面我们从源码角度来探究
Semaphore
的内心世界
简单的一个例子
实现一个简单的线程同步例子,semaphore.release()
,计数器加一,semaphore.acquire(2)
,尝试计数器减二,失败,阻塞,成功,直接返回
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class Main{
public static Semaphore semaphore = new Semaphore(0);
public static void main(String args[])throws Exception{
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new Runnable(){
@Override
public void run() {
try{
System.out.println("Thread1 step1 start");
//doSomething
semaphore.release();
System.out.println("Thread1 step1 end");
}catch(Exception e) {
e.printStackTrace();
}
}
});
pool.submit(new Runnable(){
@Override
public void run() {
try{
System.out.println("Thread2 step1 start");
//doSomething
semaphore.release();
System.out.println("Thread2 step1 end");
}catch(Exception e) {
e.printStackTrace();
}
}
});
semaphore.acquire(2);
pool.shutdown();
}
}
Semaphore类图
semaphore实现依赖AQS,可以参考抽象队列式同步器AQS详解
初始化
支持公平和非公平两种方式, permits是用来初始化state变量
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
release方法
release方法调用sync的releaseShared
public void release() {
sync.releaseShared(1);
}
releaShared方法
release方法调用tryReleaseShared
,释放资源,成功,则尝试唤醒等待的进程。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared方法
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;
}
}
acquire方法
acquire方法调用acquireSharedInterruptibly
方法,acquireSharedInterruptibly
调用doAcquireSharedInterruptibly
方法
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);
}
acquireSharedInterruptibly方法
调用tryAcquireShared
尝试获取资源,tryAcquireShared
调用NofairSync重写的nonfairTryAcquireShared
或者FairSync重写的fairTryAcquireShared
.尝试获取资源,失败则进行阻塞队列,进行阻塞等待。
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
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;
}
}
总结
总结一下工作过程
- 线程A调用Semaphore的
acquire(N)
方法,尝试获取N个资源,失败,则进入阻塞队列等待 - 其他线程调用Semaphore的release()方法,释放一个资源
- 当有N个资源,满足线程A的要求时,线程A被唤醒,同步过程结束。
参考文章
Java并发编程之美