@@ -54,10 +54,9 @@ AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列
5454private volatile int state;// 共享变量,使用volatile修饰保证线程可见性
5555```
5656
57- 状态信息通过protected类型的getState, setState,compareAndSetState进行操作
57+ 状态信息通过protected类型的 ` getState ` , ` setState ` , ` compareAndSetState ` 进行操作
5858
5959``` java
60-
6160// 返回同步状态的当前值
6261protected final int getState() {
6362 return state;
@@ -76,10 +75,125 @@ protected final boolean compareAndSetState(int expect, int update) {
7675
7776** AQS定义两种资源共享方式**
7877
79- - ** Exclusive** (独占):只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:
80- - 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
81- - 非公平锁:当线程要获取锁时,先通过 CAS 操作和队列头的线程去抢锁,如果没抢到,当前线程再加入到队列中等待唤醒。
82- - ** Share** (共享):多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。
78+ ** 1)Exclusive** (独占)
79+
80+ 只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁,ReentrantLock 同时支持两种锁,下面以 ReentrantLock 对这两种锁的定义做介绍:
81+
82+ - 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
83+ - 非公平锁:当线程要获取锁时,先通过两次 CAS 操作去抢锁,如果没抢到,当前线程再加入到队列中等待唤醒。
84+
85+ > 说明:下面这部分关于 ` ReentrantLock ` 源代码内容节选自:https://www.javadoop.com/post/AbstractQueuedSynchronizer-2,这是一篇很不错文章,推荐阅读。
86+
87+ ** 下面来看 ReentrantLock 中相关的源代码:**
88+
89+ ReentrantLock 默认采用非公平锁,因为考虑获得更好的性能,通过 boolean 来决定是否用公平锁(传入 true 用公平锁)。
90+
91+ ``` java
92+ /* * Synchronizer providing all implementation mechanics */
93+ private final Sync sync;
94+ public ReentrantLock() {
95+ // 默认非公平锁
96+ sync = new NonfairSync ();
97+ }
98+ public ReentrantLock(boolean fair) {
99+ sync = fair ? new FairSync () : new NonfairSync ();
100+ }
101+ ```
102+
103+ ReentrantLock 中公平锁的 ` lock ` 方法
104+
105+ ``` java
106+ static final class FairSync extends Sync {
107+ final void lock () {
108+ acquire(1 );
109+ }
110+ // AbstractQueuedSynchronizer.acquire(int arg)
111+ public final void acquire (int arg ) {
112+ if (! tryAcquire(arg) &&
113+ acquireQueued(addWaiter(Node . EXCLUSIVE ), arg))
114+ selfInterrupt();
115+ }
116+ protected final boolean tryAcquire (int acquires ) {
117+ final Thread current = Thread . currentThread();
118+ int c = getState();
119+ if (c == 0 ) {
120+ // 1. 和非公平锁相比,这里多了一个判断:是否有线程在等待
121+ if (! hasQueuedPredecessors() &&
122+ compareAndSetState(0 , acquires)) {
123+ setExclusiveOwnerThread(current);
124+ return true ;
125+ }
126+ }
127+ else if (current == getExclusiveOwnerThread()) {
128+ int nextc = c + acquires;
129+ if (nextc < 0 )
130+ throw new Error (" Maximum lock count exceeded" );
131+ setState(nextc);
132+ return true ;
133+ }
134+ return false ;
135+ }
136+ }
137+ ```
138+
139+ 非公平锁的 lock 方法:
140+
141+ ``` java
142+ static final class NonfairSync extends Sync {
143+ final void lock () {
144+ // 2. 和公平锁相比,这里会直接先进行一次CAS,成功就返回了
145+ if (compareAndSetState(0 , 1 ))
146+ setExclusiveOwnerThread(Thread . currentThread());
147+ else
148+ acquire(1 );
149+ }
150+ // AbstractQueuedSynchronizer.acquire(int arg)
151+ public final void acquire (int arg ) {
152+ if (! tryAcquire(arg) &&
153+ acquireQueued(addWaiter(Node . EXCLUSIVE ), arg))
154+ selfInterrupt();
155+ }
156+ protected final boolean tryAcquire (int acquires ) {
157+ return nonfairTryAcquire(acquires);
158+ }
159+ }
160+ /**
161+ * Performs non-fair tryLock. tryAcquire is implemented in
162+ * subclasses, but both need nonfair try for trylock method.
163+ */
164+ final boolean nonfairTryAcquire(int acquires) {
165+ final Thread current = Thread . currentThread();
166+ int c = getState();
167+ if (c == 0 ) {
168+ // 这里没有对阻塞队列进行判断
169+ if (compareAndSetState(0 , acquires)) {
170+ setExclusiveOwnerThread(current);
171+ return true ;
172+ }
173+ }
174+ else if (current == getExclusiveOwnerThread()) {
175+ int nextc = c + acquires;
176+ if (nextc < 0 ) // overflow
177+ throw new Error (" Maximum lock count exceeded" );
178+ setState(nextc);
179+ return true ;
180+ }
181+ return false ;
182+ }
183+ ```
184+
185+ 总结:公平锁和非公平锁只有两处不同:
186+
187+ 1 . 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。
188+ 2 . 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
189+
190+ 公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。
191+
192+ 相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。
193+
194+ ** 2)Share** (共享)
195+
196+ 多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。
83197
84198ReentrantReadWriteLock 可以看成是组合式,因为ReentrantReadWriteLock也就是读写锁允许多个线程同时对某一资源进行读。
85199
0 commit comments