之前分析过AQS的源码,但只分析了独占锁的原理。
而刚好我们可以借助Semaphore来分析共享锁。
如何使用Semaphore
public class SemaphoreDemo {
public static void main(String[] args) {
// 申请共享锁数量
Semaphore sp = new Semaphore(3);
for(int i = 0; i < 5; i++) {
new Thread(() -> {
try {
// 获取共享锁
sp.acquire();
String threadName = Thread.currentThread().getName();
// 访问API
System.out.println(threadName + " 获取许可,访问API。剩余许可数 " + sp.availablePermits());
TimeUnit.SECONDS.sleep(1);
// 释放共享锁
sp.release();
System.out.println(threadName + " 释放许可,当前可用许可数为 " + sp.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "thread-" + (i+1)).start();
}
}
}
Java SDK 里面提供了 Lock,为啥还要提供一个 Semaphore ?其实实现一个互斥锁,仅仅是 Semaphore 的部分功能,Semaphore 还有一个功能是 Lock 不容易实现的,那就是:Semaphore 可以允许多个线程访问一个临界区。
比较常见的需求就是我们工作中遇到的连接池、对象池、线程池等等池化资源。其中,你可能最熟悉数据库连接池,在同一时刻,一定是允许多个线程同时使用连接池的,当然,每个连接在被释放前,是不允许其他线程使用的。
比如上面的代码就演示了同时最多只允许3个线程访问API。
如何依托AQS实现Semaphore
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits);
}
// 获取锁
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining