簡單的瞭解一下AQS吧

什麼是AQS

AQS,即AbstractQueuedSynchronizer,是一套定義了多線程訪問共享資源的同步器框架。在JDK的併發包中不少類都是基於AQS進行實現的,好比ReentrantLockCountDownLatch等。node

AQS中的設計模式

若是單單只是看AQS類中的代碼的haul可能會產生不少疑惑,由於類中不少方法都是隻有方法體,具體的實現須要到子類中才能看到。設計模式

模板方法模式

在咱們日常的開發中會常常遇到一個問題,當咱們接到一個需求時,在整理大致思路時會很清晰。可是當實際實現的時候會發現問題不少,有些步驟實現是沒有辦法肯定下來的。會根據不一樣的需求進行更改。多線程

這種邏輯流程肯定,可是具體實現可能不一樣的問題能夠經過模板方法模式來解決。併發

所謂的模板方法模式就是定義一個操做的流程骨架,肯定調用流程。可是具體的實現則交給子類去完成。模板方法模式就是利用了面向對象中的多態特性。框架

在模板方法模式中有兩個重要的角色,一個是抽象模板類,另外一個就是具體的實現類。ide

抽象模板類

抽象模板類用於定義業務流程,在該類中定義了一系列完成業務所需的方法。可以肯定的方法能夠在抽象類中實現邏輯。不能肯定的只是定義好方法,具體的實現由子類完成。ui

以AQS舉例,AbstractQueuedSynchronizer被定義爲抽象類,其中一部分方法只是定義了方法體:線程

protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
    
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

儘管這部分方法並無提供具體的實現,可是AQS中的其餘方法仍是直接調用了該方法。設計

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

跟句抽象類的特性,若是要使用這些方法的話就必須在子類繼承AQS並實現這些抽象方法。這樣的方法類被稱爲模板類。code

實現類

模板類的抽象方法的邏輯實現是在子類中完成的,不一樣的子類能夠根據具體的需求進行個性化的實現。

好比ReentrantLock中Sync和FairSync對於tryAcquire的實現:

Sync:

protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
    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;
    }

FairSync:

protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            //這裏多了一次是否存在等待更長時間線程的判斷
            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;
    }

這樣的類被稱爲實現類。

AQS中的模板方法

AQS就是典型的應用模板方法模式的例子,若是咱們要經過AQS來實現一個同步類。那麼咱們須要實現如下方法:

tryAcquire(int) 
tryRelease(int) 
tryAcquireShared(int) 
tryReleaseShared(int) 
isHeldExclusively()

部分參數解析

state

state參數是很是重要的一個參數,AQS的鎖狀態就是依賴於改參數實現的。

AQS中對鎖的操做是利用CAS進行實現,而cas主要操做的對象就是state參數。當state=0時表示能夠獲取鎖,而當state!=0時則表示已經進行了加鎖操做。

可重入鎖的實現也依賴於該參數,當持有鎖的線程再次獲取一次鎖時便將state的值加一,而每一次釋放一次鎖則進行減一操做,只有當state=0時纔算是釋放鎖完畢。

Node

static final class Node {

        static final Node SHARED = new Node();

        static final Node EXCLUSIVE = null;

        static final int CANCELLED =  1;

        static final int SIGNAL    = -1;

        static final int CONDITION = -2;

        static final int PROPAGATE = -3;

        volatile int waitStatus;

        volatile Node prev;

        volatile Node next;

        volatile Thread thread;

        Node nextWaiter;
}

Node用於保存獲取鎖失敗時

Node.SHARED和Node.EXCLUSIVE

在AQS的具體實現中存在兩種不一樣模式的鎖:排他鎖和共享鎖

通常共享鎖主要用於讀操做,表示讀操做能夠是多個線程同時進行,而不會阻塞;排他鎖主要用於寫操做,會進行阻塞

而排他鎖和共享鎖的實現就依賴於Node.SHARED和Node.EXCLUSIVE區分。好比ReentrantReadWriteLock

waitStatus

waitStatus用於表示當前節點所處的狀態。

  • 初始狀態:節點初始狀態值被初始化爲0,若是是經過condition註冊的節點其初始狀態爲-2(CONDITION)
  • CANCELLED:static final int CANCELLED = 1;因爲超時或者中斷等緣由使得當前節點被標記位取消狀態。通常來講被標記爲取消狀態的節點不會再去競爭鎖而且不能轉換爲其餘狀態。
  • SIGNAL:static final int SIGNAL = -1;當前節點的後繼節點經過park被阻塞(或者將要被阻塞)。那麼在當前節點釋放或者取消時須要經過unpark取消阻塞。
  • CONDITION:static final int CONDITION = -2;將節點放在condition隊列中是須要標識其狀態爲CONDITION。
  • PROPAGATE:static final int PROPAGATE = -3;該狀態值用於在共享狀態下,當共享狀態的鎖被釋放後,該操做會被傳播到其餘節點。

prev next

prev和next分別用於記錄前驅節點和後繼節點

重要方法解析

tryAcquire

protected boolean tryAcquire(int arg);

tryAcquire字面意思很明確,就是嘗試獲取鎖。獲取鎖成功則返回true,獲取鎖失敗則將該線程放入等待隊列中,等待佔用資源的線程被釋放。

在JDK中明肯定義tryAcquire方法用於獲取的處於獨佔模式下的鎖。若是不是獨佔模式則拋出異常UnsupportedOperationException

該方法須要被重寫。

該方法共享模式版本爲protected int tryAcquireShared(int arg).

tryRelease

protected boolean tryRelease(int arg);

該方法用於在獨佔模式下經過cas嘗試設置state狀態值,用於釋放鎖操做。

修改值成功則返回true。若是不是獨佔模式則拋出異常UnsupportedOperationException

該方法須要被重寫。

該方法的共享模式方法爲protected boolean tryReleaseShared(int arg)

isHeldExclusively

該方法用於來判斷是否當前線程正在以獨佔模式進行同步操做。

setState和compareAndSetState

setState和compareAndSetState兩個方法都是對state參數的值進行設置。

不一樣之處在於compareAndSetState主要用於獲取鎖時修改狀態值,由於獲取鎖時存在競爭問題因此須要原子操做獲取。

而setState操做用於在釋放鎖是修改state的值,釋放鎖時只有持有鎖的線程會進行釋放,不存在競爭問題,不須要原子操做。

動手實現一個同步類

如今咱們來實現一個咱們本身的同步類,一個不可重入的獨佔鎖。

public class MyLock implements Lock {

    static class Sync extends AbstractQueuedSynchronizer{
        @Override
        protected boolean tryAcquire(int arg) {
            //這裏只有當state=0時才能獲取鎖 表示該同步類不可重入
            if(compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if(getState()!=1){
                //沒法再被釋放
                throw new IllegalMonitorStateException();
            }
            setState(0);
            setExclusiveOwnerThread(null);
            return true;
        }

        @Override
        protected boolean isHeldExclusively() {
            return getState()==1 || 
                    getExclusiveOwnerThread()==Thread.currentThread();
        }

        // 返回一個Condition,每一個condition都包含了一個condition隊列
        Condition newCondition() {
            return new ConditionObject();
        }
    }

    private final Sync sync = new Sync();
    
    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.release(0);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}

即便咱們並不知道AQS的內部實現,只須要了解AQS中的幾個方法做用並在子類中重寫這些方法就能設計出一個簡單的同步類。

相關文章
相關標籤/搜索