mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-06-16 18:10:13 +08:00
Update AQS.md
This commit is contained in:
parent
a5c53a4225
commit
d6ad33c38e
@ -54,10 +54,9 @@ AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列
|
||||
private volatile int state;//共享变量,使用volatile修饰保证线程可见性
|
||||
```
|
||||
|
||||
状态信息通过protected类型的getState,setState,compareAndSetState进行操作
|
||||
状态信息通过protected类型的`getState`,`setState`,`compareAndSetState`进行操作
|
||||
|
||||
```java
|
||||
|
||||
//返回同步状态的当前值
|
||||
protected final int getState() {
|
||||
return state;
|
||||
@ -76,10 +75,125 @@ protected final boolean compareAndSetState(int expect, int update) {
|
||||
|
||||
**AQS定义两种资源共享方式**
|
||||
|
||||
- **Exclusive**(独占):只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:
|
||||
**1)Exclusive**(独占)
|
||||
|
||||
只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁,ReentrantLock 同时支持两种锁,下面以 ReentrantLock 对这两种锁的定义做介绍:
|
||||
|
||||
- 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
|
||||
- 非公平锁:当线程要获取锁时,先通过 CAS 操作和队列头的线程去抢锁,如果没抢到,当前线程再加入到队列中等待唤醒。
|
||||
- **Share**(共享):多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。
|
||||
- 非公平锁:当线程要获取锁时,先通过两次 CAS 操作去抢锁,如果没抢到,当前线程再加入到队列中等待唤醒。
|
||||
|
||||
> 说明:下面这部分关于 `ReentrantLock` 源代码内容节选自:https://www.javadoop.com/post/AbstractQueuedSynchronizer-2,这是一篇很不错文章,推荐阅读。
|
||||
|
||||
**下面来看 ReentrantLock 中相关的源代码:**
|
||||
|
||||
ReentrantLock 默认采用非公平锁,因为考虑获得更好的性能,通过 boolean 来决定是否用公平锁(传入 true 用公平锁)。
|
||||
|
||||
```java
|
||||
/** Synchronizer providing all implementation mechanics */
|
||||
private final Sync sync;
|
||||
public ReentrantLock() {
|
||||
// 默认非公平锁
|
||||
sync = new NonfairSync();
|
||||
}
|
||||
public ReentrantLock(boolean fair) {
|
||||
sync = fair ? new FairSync() : new NonfairSync();
|
||||
}
|
||||
```
|
||||
|
||||
ReentrantLock 中公平锁的 `lock` 方法
|
||||
|
||||
```java
|
||||
static final class FairSync extends Sync {
|
||||
final void lock() {
|
||||
acquire(1);
|
||||
}
|
||||
// AbstractQueuedSynchronizer.acquire(int arg)
|
||||
public final void acquire(int arg) {
|
||||
if (!tryAcquire(arg) &&
|
||||
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
|
||||
selfInterrupt();
|
||||
}
|
||||
protected final boolean tryAcquire(int acquires) {
|
||||
final Thread current = Thread.currentThread();
|
||||
int c = getState();
|
||||
if (c == 0) {
|
||||
// 1. 和非公平锁相比,这里多了一个判断:是否有线程在等待
|
||||
if (!hasQueuedPredecessors() &&
|
||||
compareAndSetState(0, acquires)) {
|
||||
setExclusiveOwnerThread(current);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (current == getExclusiveOwnerThread()) {
|
||||
int nextc = c + acquires;
|
||||
if (nextc < 0)
|
||||
throw new Error("Maximum lock count exceeded");
|
||||
setState(nextc);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
非公平锁的 lock 方法:
|
||||
|
||||
```java
|
||||
static final class NonfairSync extends Sync {
|
||||
final void lock() {
|
||||
// 2. 和公平锁相比,这里会直接先进行一次CAS,成功就返回了
|
||||
if (compareAndSetState(0, 1))
|
||||
setExclusiveOwnerThread(Thread.currentThread());
|
||||
else
|
||||
acquire(1);
|
||||
}
|
||||
// AbstractQueuedSynchronizer.acquire(int arg)
|
||||
public final void acquire(int arg) {
|
||||
if (!tryAcquire(arg) &&
|
||||
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
|
||||
selfInterrupt();
|
||||
}
|
||||
protected final boolean tryAcquire(int acquires) {
|
||||
return nonfairTryAcquire(acquires);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Performs non-fair tryLock. tryAcquire is implemented in
|
||||
* subclasses, but both need nonfair try for trylock method.
|
||||
*/
|
||||
final boolean nonfairTryAcquire(int acquires) {
|
||||
final Thread current = Thread.currentThread();
|
||||
int c = getState();
|
||||
if (c == 0) {
|
||||
// 这里没有对阻塞队列进行判断
|
||||
if (compareAndSetState(0, acquires)) {
|
||||
setExclusiveOwnerThread(current);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (current == getExclusiveOwnerThread()) {
|
||||
int nextc = c + acquires;
|
||||
if (nextc < 0) // overflow
|
||||
throw new Error("Maximum lock count exceeded");
|
||||
setState(nextc);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
总结:公平锁和非公平锁只有两处不同:
|
||||
|
||||
1. 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。
|
||||
2. 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
|
||||
|
||||
公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。
|
||||
|
||||
相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。
|
||||
|
||||
**2)Share**(共享)
|
||||
|
||||
多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。
|
||||
|
||||
ReentrantReadWriteLock 可以看成是组合式,因为ReentrantReadWriteLock也就是读写锁允许多个线程同时对某一资源进行读。
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user