深入理解AQS


簡介

AQS是Java並發包中很重要的一個抽象類,我們所使用的ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore等都是基於AQS來實現的。

1.1 實現原理

AQS中維護了一個state變量這個表示共享的資源,以及一個CHL隊列(多線程爭奪資源的時候被阻塞的線程將會被放進這個隊列)。這個隊列不需要我們去維護,我們需要關注的幾個點就是state變量的獲取和釋放規則。
state 變量被聲明的類型是volatile類型保證多線程下的資源可見性,state=1表示當前資源被占用。
state值的更新是通過CAS操作來保證它修改的安全性。

AQS中提供了以下主要的方法:

  • getState():獲取鎖的標志state值
  • setState():設置鎖的標志state值
  • tryAcquire(int):獨占方式獲取鎖。嘗試獲取資源,成功則返回true,失敗則返回false。
  • tryRelease(int):獨占方式釋放鎖。嘗試釋放資源,成功則返回true,失敗則返回false。

這里對應獨占鎖還有兩個方法是對應共享鎖的 tryShare方法。

private volatile int state;//共享變量,使用volatile修飾保證線程可見性
//返回同步狀態的當前值
protected final int getState() {
        return state;
}
 // 設置同步狀態的值
protected final void setState(int newState) {
        state = newState;
}
//原子地(CAS操作)將同步狀態值設置為給定值update如果當前同步狀態的值等於expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

2.1 一些同步器的實現

ReentrantLock 默認采用非公平鎖,因為考慮獲得更好的性能,通過 boolean 來決定是否用公平鎖(傳入 true 用公平鎖)。

/** 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 方法

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 方法:

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;
}

總結:公平鎖和非公平鎖只有兩處不同:

非公平鎖在調用 lock 后,首先就會調用 CAS 進行一次搶鎖,如果這個時候恰巧鎖沒有被占用,那么直接就獲取到鎖返回了。
非公平鎖在 CAS 失敗后,和公平鎖一樣都會進入到 tryAcquire 方法,在 tryAcquire 方法中,如果發現鎖這個時候被釋放了(state == 0),非公平鎖會直接 CAS 搶鎖,但是公平鎖會判斷等待隊列是否有線程處於等待狀態,如果有則不去搶鎖,乖乖排到后面。
公平鎖和非公平鎖就這兩點區別,如果這兩次 CAS 都不成功,那么后面非公平鎖和公平鎖是一樣的,都要進入到阻塞隊列等待喚醒。

相對來說,非公平鎖會有更好的性能,因為它的吞吐量比較大。當然,非公平鎖讓獲取鎖的時間變得更加不確定,可能會導致在阻塞隊列中的線程長期處於飢餓狀態。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM