JUC - AbstractQueuedSynchronizer(AQS) 源碼分析

簡介

AbstractQueuedSynchronizer,同步器,以下簡稱AQS。本文從源碼分析AQS的核心方法和實現原理。

AQS內部有兩組重要的成員變量:

  1. int類型的status變量,通過CAS操作(詳見:CAS深度分析)改變status值來控制當前線程能否訪問資源以及併發數量。
  2. Node類型的head和tail兩個變量,兩個變量維護了一個FIFO的同步隊列,將獲取訪問權限失敗的線程構造成Node節點加入隊列中,釋放資源時再來喚醒隊列中阻塞的線程。(Node類型主要包涵節點的狀態,當前線程的引用,以及前驅節點和後置節點的引用)

使用方式

AQS在使用時一般是作爲自定義同步工具的內部類,實現AQS中可重寫的方法,來自定義獲取以及釋放鎖的方式,在自定義同步工具類中,調用AQS中提供給使用者的模版方法,來控制鎖的獲取和釋放。

提供給使用者調用的模版方法:

獨佔式

void acquire(int arg)
以獨佔模式獲取對象,忽略中斷。

void acquireInterruptibly(int arg) throws InterruptedException
以獨佔模式獲取對象,如果被中斷則中止,拋出InterruptedException。

boolean tryAcquireNanos(int arg,long nanosTimeout) throws InterruptedException
試圖以獨佔模式獲取對象,如果被中斷則中止,拋出InterruptedException,如果到了給定超時時間,則會返回失敗。

boolean release(int arg)
以獨佔模式釋放對象。

共享式

void acquireShared(int arg)
以共享模式獲取對象,忽略中斷。

void acquireSharedInterruptibly(int arg) throws InterruptedException
以共享模式獲取對象,如果被中斷則中止,拋出InterruptedException。

boolean tryAcquireSharedNanos(int arg,long nanosTimeout) throws InterruptedException
試圖以共享模式獲取對象,如果被中斷則中止,拋出InterruptedException,如果到了給定超時時間,則會返回失敗。

boolean releaseShared(int arg)
以共享模式釋放對象。

同步器可重寫的方法:

獨佔式

protected boolean tryAcquire(int arg)   
獨佔的獲取這個狀態。這個方法的實現需要查詢當前狀態是否允許獲取,然後再進行獲取(使用compareAndSetState來做)狀態。

protected boolean tryRelease(int arg)   
釋放狀態。

共享式

protected int tryAcquireShared(int arg) 
共享的模式下獲取狀態。

protected boolean tryReleaseShared(int arg) 
共享的模式下釋放狀態。

核心方法及源碼分析

獨佔模式的獲取和釋放

* void acquire(int arg)

以獨佔模式獲取對象,忽略中斷。

    public final void acquire(int arg) {
        if (!tryAcquire(arg) //通過CAS更新status嘗試獲取
            && acquireQueued(addWaiter(Node.EXCLUSIVE) //獲取鎖失敗後添加到同步隊列
            , arg)) //在隊列中自旋直至成功獲取鎖才返回,線程可能被反覆park、unpark,直到獲取鎖,返回值代表是否被中斷過
            selfInterrupt(); //之前被中斷過則還原中斷狀態
    }
    private Node addWaiter(Node mode) { //添加到同步隊列
        Node node = new Node(Thread.currentThread(), mode); //把當前線程構造成Node節點
        Node pred = tail; //獲取當前的tail節點
        if (pred != null) {
            node.prev = pred; //當前節點前驅節點設置爲當前的tail節點
            if (compareAndSetTail(pred, node)) { //CAS操作,如果內存中當前tail節點的值是pred,則將tail節點指向當前新節點node,返回true代表新節點成功進入隊列
                pred.next = node; //將前驅節點的next設置爲node節點,這個節點無法和tail節點一起通過CAS操作設置,next節點僅僅是爲了優化,當next爲空時,始終可以通過tail節點的pred字段從後向前遍歷所有節點。
                return node;
            }
        }
        enq(node); //失敗時通過無線循環直至成功添加節點
        return node;
    }

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { //當前沒有初始化過隊列則創建初始化新節點,再通過循環添加當前線程節點
                if (compareAndSetHead(new Node())) //原子地設置頭結點
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
    final boolean acquireQueued(final Node node, int arg) { //使節點阻塞自旋,直至獲取到鎖,才返回。
        boolean failed = true; //當前獲取是否失敗
        try {
            boolean interrupted = false; //當前獲取是否被中斷
            for (;;) {
                final Node p = node.predecessor(); //獲取有效的前驅節點,前驅節點一定不爲null
                if (p == head //head節點要麼是初始化的節點,要麼代表當前成功獲取到鎖的線程,所以前驅節點是head的時候,當前節點才應該嘗試去獲取鎖 
                    && tryAcquire(arg) ) { //嘗試獲取鎖
                    setHead(node); //設置當前節點爲head
                    p.next = null; //之前老的head節點next引用置空 help GC 
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) //查看當前節點是否應該被park 
                    && parkAndCheckInterrupt()) //park當前線程,直到被其他線程unpark,如果被中斷喚醒,則返回true,由於acquire忽略中斷所以重新嘗試獲取鎖,獲取失敗失敗重新park。
                    interrupted = true; //線程被中斷過
            }
        } finally {
            if (failed)
                cancelAcquire(node); //取消當前線程
        }
    }

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //查看當前節點是否應該被park 
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL) //前驅節點是signal狀態, park當前線程
            return true;
        if (ws > 0) { //前驅結點已取消
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0); //前驅結點已取消,向前遍歷直到找到一個非取消結點,同時將取消節點的前後節點相連
            pred.next = node;
        } else {
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //將當前節點設置爲signal狀態,表明後繼節點需要unpark
        }
        return false;
    }

    private final boolean parkAndCheckInterrupt() { //park當前線程,並返回當前中斷狀態
        LockSupport.park(this); //掛起當前線程,當中斷或者被其他線程unpark這個線程則返回,不區分park和unpark的先後
        return Thread.interrupted(); //清空並返回中斷狀態,保證後續仍然可以park,返回的值將決定完成獲取後是否需要恢復中斷狀態
    }
    private void cancelAcquire(Node node) { //取消當前節點
        if (node == null)
            return;
        node.thread = null;
        Node pred = node.prev;
        while (pred.waitStatus > 0) //跳過已經取消的前驅節點
            node.prev = pred = pred.prev;

        Node predNext = pred.next; //predNext是需要移除的結點
        node.waitStatus = Node.CANCELLED; //無條件設置節點狀態爲取消
        if (node == tail && compareAndSetTail(node, pred)) { //如果處於鏈尾,直接移除,再修復前驅的連接關係
            compareAndSetNext(pred, predNext, null);
        } else {node有後繼。用前驅的next指針指向他,這樣他會得到正確的signal信號,否則喚醒他來傳播信號。
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) { //如果true,說明node有一個需要signal的前驅,讓這個前驅指向node的後繼,完整節點的鏈接關係
                Node next = node.next;
                if (next != null && next.waitStatus <= 0) //node有後繼
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node); //在執行到上面一句將waitStatus置CANCELLED之前,鎖被釋放,該node線程被喚醒,則釋放鎖線程的unparkSuccessor不能起到預期作用,所以這裏需要調用unparkSuccessor.即使此時持有鎖的線程沒有釋放鎖也不會有嚴重後果,被unpark的線程在獲取鎖失敗後會繼續park
            }
            node.next = node; // help GC
        }
    }

* void acquireInterruptibly(int arg) throws InterruptedException

以獨佔模式獲取對象,如果被中斷則中止,拋出InterruptedException。

    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted()) //被中斷則清空中斷狀態並拋出異常
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

    private void doAcquireInterruptibly(int arg)
        throws InterruptedException { //和acquireQueued的區別就是被中斷則不再重新獲取,直接結束
        final Node node = addWaiter(Node.EXCLUSIVE); //將節點添加進同步隊列
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt()) //park當前線程,直到被其他線程unpark,如果被中斷喚醒,則返回true
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

* boolean tryAcquireNanos(int arg,long nanosTimeout) throws InterruptedException

試圖以獨佔模式獲取對象,如果被中斷則中止,拋出InterruptedException,如果到了給定超時時間,則會返回失敗。

    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted()) //被中斷則清空中斷狀態並拋出異常
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }

    private boolean doAcquireNanos(int arg, long nanosTimeout) //doAcquireInterruptibly
        throws InterruptedException {
        long lastTime = System.nanoTime(); //獲取當前時間
        final Node node = addWaiter(Node.EXCLUSIVE); //將節點添加進同步隊列
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
                if (nanosTimeout <= 0) //如果沒有獲取到,且超時時間小於等於0則返回失敗
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold) //spinForTimeoutThreshold爲1000納秒,當休眠時間小於等於1000納秒時,由於非常短的超時等待無法做到十分精確,也會帶來額外的線程上下文切換,所以這裏直接進入快速的自旋。否則park線程
                    LockSupport.parkNanos(this, nanosTimeout);
                long now = System.nanoTime(); //獲取unpark後的時間
                nanosTimeout -= now - lastTime; //將超時時間減去獲取鎖以及線程park的時間,得到之後還需要park的時間。
                lastTime = now;
                if (Thread.interrupted()) //被中斷則清空中斷狀態並拋出異常
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

* boolean release(int arg)

以獨佔模式釋放對象。

    public final boolean release(int arg) {
        if (tryRelease(arg)) { //如果修改status狀態釋放鎖成功
            Node h = head; //head是初始化的節點或代表當前佔有鎖的線程,所以要unparkhead的有效後繼節點
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); //unparkhead節點的有效後繼節點
            return true;
        }
        return false;
    }

    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0) //重置頭節點的狀態爲0
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        if (s == null || s.waitStatus > 0) { //後繼節點是null或者已取消
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev) //節點的next字段用於優化,當next鏈接沒有時,仍然需要從tail節點向前遍歷檢查,獲取隊列最前面的有效節點作爲需要unpark的節點
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);//unpark同步隊列最前面的有效節點
    }

共享模式的獲取和釋放

* void acquireShared(int arg)

以共享模式獲取對象,忽略中斷。

    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0) //tryAcquireShared大於等於0則共享鎖獲取成功
            doAcquireShared(arg);
    }

    private void doAcquireShared(int arg) { //和獨佔式的acquireQueued方法區別就是獲取成功的節點會繼續unpark後繼節點,將共享狀態向後傳播
        final Node node = addWaiter(Node.SHARED); //添加到同步隊列
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) { //節點的前驅是head
                    int r = tryAcquireShared(arg);
                    if (r >= 0) { //獲取鎖成功
                        setHeadAndPropagate(node, r); //設置頭節點並unpark後繼節點,傳播共享狀態
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

    private void setHeadAndPropagate(Node node, int propagate) { //設置頭節點並unpark後繼節點,傳播共享狀態。參數propagate代表當前還有可以獲取的鎖數量
        Node h = head; 
        setHead(node); //設置當前節點爲head節點
        if (propagate > 0 || h == null || h.waitStatus < 0) {//當前有資源可以獲取,或當前節點可以喚醒後繼節點 
            Node s = node.next;
            if (s == null || s.isShared()) //後繼節點是共享的則unpark後繼節點
                doReleaseShared(); //unpark後繼節點
        }
    }

* void acquireSharedInterruptibly(int arg) throws InterruptedException

以共享模式獲取對象,如果被中斷則中止,拋出InterruptedException。

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

* boolean tryAcquireSharedNanos(int arg,long nanosTimeout) throws InterruptedException

試圖以共享模式獲取對象,如果被中斷則中止,拋出InterruptedException,如果到了給定超時時間,則會返回失敗。

    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquireShared(arg) >= 0 ||
            doAcquireSharedNanos(arg, nanosTimeout);
    }

    private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {

        long lastTime = System.nanoTime();
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return true;
                    }
                }
                if (nanosTimeout <= 0)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                long now = System.nanoTime();
                nanosTimeout -= now - lastTime;
                lastTime = now;
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

* boolean releaseShared(int arg)

以共享模式釋放對象。

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared(); //unpark後繼節點,傳播共享狀態
            return true;
        }
        return false;
    }

    private void doReleaseShared() { //unpark後繼節點,傳播共享狀態
        for (;;) {
            Node h = head;
            if (h != null && h != tail) { //如果隊列中有節點
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) { //頭節點狀態是signal,則重置狀態爲0,並unpark後繼節點
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) //如果狀態爲0,則將0設置爲PROPAGATE狀態
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章