引言
Semaphore 就像是饭堂,饭堂有固定的桌位,在吃饭高峰期时就要来早点抢位,不然桌位坐满了就得等别人吃完才有桌位吃饭;
测试示例
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3); // 设置可用资源数 3
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 抢占资源
System.out.println(Thread.currentThread().getName() + " 抢到桌位,开始吃饭");
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " 吃完饭,离开桌位");
semaphore.release(); // 释放
}
}, "t" + i).start();
}
}
}
输出结果:
以上示例为总3张桌位,5批人来吃饭,0、1、4先抢到桌位入座,2、3等别人离开后入座
Semaphore 源码
public class Semaphore implements java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {}
// 不公平版本 (默认)
static final class NonfairSync extends Sync {}
// 公平版本
static final class FairSync extends Sync {}
}
Semaphore 内部定义了一个 Sync 内部类实现了 AbstractQueuedSynchronizer 同步器;
派生出 NonfairSync 和 FairSync两个版本的实现
acquire 抢占资源
semaphore.acquire() 方法用于抢占资源
跟踪源码 acquire() 调用最终会进入Sync.nonfairTryAcquireShared(),并且对state的值原子性减1,state是 AbstractQueuedSynchronizer 维护的一个状态值,也可以说是计数器,可以说AbstractQueuedSynchronizer同步器的实现很大程度都依靠 state 来实现;前3个抢占到资源的线程每个都对 state 的值减1;
当第4个线程进来时,并没有可用的资源使用,此时 state 的值为0,进入doAcquireSharedInterruptibly() 方法,对于没有抢占到资源的线程会维护成一个链表的形式,最后调用 LockSupport.park(this) 挂起该线程;
release 释放资源
释放资源就是对 state 的值加 1;并且调用 LockSupport.unpark() 唤醒链表中的其他线程
总结
Semaphore 内部维护了一个Sync 实现 AbstractQueuedSynchronizer 同步器来实现的功能,AbstractQueuedSynchronizer 还真是一个好东西;
Semaphore → AbstractQueuedSynchronizer → (state、链表、LockSupport)