Semaphore 源码分析
1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE 方便,所以在 github 上提供JDK1.8 的源码、详细的注释及测试用例。欢迎大家 star、fork !
2. 由于个人水平有限,对源码的分析理解可能存在偏差或不透彻的地方还请大家在评论区指出,谢谢!
1. Semephore 简单介绍
一般来说,在 Java 中比较常用的同步工具就是 Lock 和 Synchronized 但是 Java 也加入了很多新的机制比如这里提到的信号量。他其实给人的感觉就是操作系统中的信号量,如果一个线程要运行需要获取一个资源进行一次 P 操作,在这里就是调用 acquire 方法,然后运行结束后调用 V 操作,释放这个资源以供其他线程使用,这里的 release 方法。那么如果资源都被其他线程抢光了,那么这个线程只能处于等待状态。也就是 P 操作返回的 -1 。
上面我们所说的信号量是多值信号量,主要用于进程之间的同步。还有一种称之为二值信号量,也就是操作系统中常提到的 mutex 。对,此时他就是锁!因为做了一个 P 操作后,只有进行 V 操作其他线程才能进入临界区。此时和 Lock 作用一样了。
下面写个简单的程序来感受一下这个同步工具。
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 4; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}).start();
}
}
}
最后输出的如下:
先打印了
Thread-0
Thread-1
隔了一秒打印了
Thread-2
Thread-3
可以看到临界区内只有两个线程同时执行。其实当我们理解了上一篇文章中的 AQS 我们大概也能猜到 Semaphore 的实现原理,也是借助于 AQS 中的 state 属性,下面具体分析一下 Semaphore 的结构及原理。
2. 基本结构
1. 继承
null
2. 实现
Serializable 感觉好多类都能序列化啊!!有没有觉得。
3. 主要字段
private final Sync sync;
好的,就一个锁字段,真干脆,那么证明前面的猜测也是对的,这个 Sync 肯定是一个内部类,继承了 AQS。在上面一篇文章中我们看到了一些 AQS 中的共享锁我没有分析,其实共享锁就是放在这使用的,所以在这篇文章中主要就是介绍 AQS 中剩下的共享锁部分。看一下整体结构。
有没有觉得很熟悉,简直和 ReentrantLock 结构极其相似。Semaphore的内部类公平锁(FairSync)和非公平锁(NoFairSync)各自实现不同的获取锁方法即tryAcquireShared(int arg),毕竟公平锁和非公平锁的获取稍后不同,而释放锁tryReleaseShared(int arg)的操作交由Sync实现,因为释放操作都是