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修饰保证线程可见性
|
private volatile int state;//共享变量,使用volatile修饰保证线程可见性
|
||||||
```
|
```
|
||||||
|
|
||||||
状态信息通过protected类型的getState,setState,compareAndSetState进行操作
|
状态信息通过protected类型的`getState`,`setState`,`compareAndSetState`进行操作
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
|
||||||
//返回同步状态的当前值
|
//返回同步状态的当前值
|
||||||
protected final int getState() {
|
protected final int getState() {
|
||||||
return state;
|
return state;
|
||||||
@ -76,10 +75,125 @@ protected final boolean compareAndSetState(int expect, int update) {
|
|||||||
|
|
||||||
**AQS定义两种资源共享方式**
|
**AQS定义两种资源共享方式**
|
||||||
|
|
||||||
- **Exclusive**(独占):只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:
|
**1)Exclusive**(独占)
|
||||||
- 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
|
|
||||||
- 非公平锁:当线程要获取锁时,先通过 CAS 操作和队列头的线程去抢锁,如果没抢到,当前线程再加入到队列中等待唤醒。
|
只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁,ReentrantLock 同时支持两种锁,下面以 ReentrantLock 对这两种锁的定义做介绍:
|
||||||
- **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也就是读写锁允许多个线程同时对某一资源进行读。
|
ReentrantReadWriteLock 可以看成是组合式,因为ReentrantReadWriteLock也就是读写锁允许多个线程同时对某一资源进行读。
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user