AQS源碼解讀(AbstractQueuedSynchronizer)

首先類看一下AQS的靜態內部類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;
        /** 這個常量代表線程正在等待condition */
        static final int CONDITION = -2;
        /**
         * 這個常量代表下一個acquireShard(獲取共享鎖)要無條件傳播?
         */
        static final int PROPAGATE = -3;

        /**
         * 這個變量代表等待變量,它的值只能取上面定義的幾個常量CANCELLED ,SIGNAL,CONDITION ,PROPAGATE 
         */
        volatile int waitStatus;

        /**
         * 這個變量指向前一個節點,可以看出Node是雙向鏈表中一個節點的數據類型
         */
        volatile Node prev;

        /**
         * 這個變量指向後一個節點
         */
        volatile Node next;

        /**
         * 線程,爲了將線程放入隊列中管理起來,用隊列的節點Node包裝了線程
         */
        volatile Thread thread;

        /**
         * 指下一個等待condition的node,不知道怎麼等待condition 
         */
        Node nextWaiter;

        /**
         * 如果nextWaiter == SHARED就說明是共享節點 
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * 獲取前一個節點Node
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // 這個構造方法通常用來構造列表頭結點或者共享節點
        }

        Node(Thread thread, Node mode) {     // 用於構造等待隊列的節點
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // 用到condition的時候用
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

AQS的變量

    /**
     * 等待隊列的頭結點
     */
    private transient volatile Node head;

    /**
     * 等待隊列的尾節點
     */
    private transient volatile Node tail;

    /**
     * AQS維護的狀態值,表示當前線程獲取鎖的次數
     */
    private volatile int state;

    /**
     * 這是線程自旋的等待時間,單位是納秒
     */
    static final long spinForTimeoutThreshold = 1000L;

AQS的變量很簡單,就首尾節點和狀態值,而它的成員方法就是對等待隊列和狀態值的操作。

AQS成員方法解讀

compareAndSetState

protected final boolean compareAndSetState(int expect, int update) {
        // 這裏用的是native方法,不做深入
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

這個方法的用CAS的方式修改state值,而state是volatile修飾的,因此在多線程的情況下也能夠保證線程安全。

compareAndSetHead、compareAndSetTail


    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }


    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }

這兩個方法也是用CAS線程安全的方式設置AQS的等待隊列頭尾節點

compareAndSetWaitStatus

private static final boolean compareAndSetWaitStatus(Node node,
                                                         int expect,
                                                         int update) {
        return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                        expect, update);
    }

這個方法也是用CAS線程安全的方式設置節點的狀態waitStatus

compareAndSetNext

        private static final boolean compareAndSetNext(Node node,
                                                   Node expect,
                                                   Node update) {
        return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
    }

用於設置Node的next成員變量

setHead

private void setHead(Node node) {
        head = node;
        // 爲什麼頭節點中沒有線程呢?
        node.thread = null;
        // 因爲是頭節點,所以前面不會有節點
        node.prev = null;
    }

將一個節點設置爲隊列的頭節點

enq

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

這個方法的作用就是向等待隊列插入節點,注意這個方法如果插入不成功就會不斷嘗試直到成功爲止

addWaiter

private Node addWaiter(Node mode) {
        // 將當前線程創建一個新節點
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        // 先試着插入一次,插入成功就返回插入的節點,不成功就用enq插到成功爲止
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

這個方法還是將節點插入到等待隊列,比enq多出一步創建節點而已

unparkSuccessor

private void unparkSuccessor(Node node) {
        /*
         * 如果節點的狀態爲負數即SIGNAL,CONDITION ,PROPAGATE,就把狀態清零
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * 找到該節點後面一個處於阻塞狀態(SIGNAL,CONDITION ,PROPAGATE)的節點,將它包含的線程喚醒
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            // 這裏爲什麼要從後往前找?
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread); // 喚醒線程
    }

該方法的作用就是喚醒當前節點後面一個被阻塞節點中的線程

doReleaseShared

private void doReleaseShared() {
        for (;;) {
            Node h = head;
            // 如果頭節點的狀態爲,SIGNAL喚醒頭節點後面的一個阻塞節點
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // 這裏通過一個for循環來保證線程安全性
                break;
        }
    }

這個方法貌似用於釋放共享鎖,不明白爲什麼喚醒頭節點後面的線程就可以釋放共享鎖?

setHeadAndPropagate

private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // 保留前一個頭節點
        setHead(node); // 設置新的節點爲頭節點
        /*
         * propagate 是tryAcquireShared返回的值,不知道表示什麼,這裏的邏輯還不明確
         * 
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

該方法的作用是:1、設置頭節點。2、如果後繼節點處於共享阻塞狀態就釋放共享鎖?

cancelAcquire

/**
     private void cancelAcquire(Node node) {
        if (node == null)
            return;

        node.thread = null;

        // 從當前節點向前找,找到狀態不爲取消CANCELLED的節點pred
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        // 保存pred節點的下個節點
        Node predNext = pred.next;

        // 將當前節點的狀態設置爲CANCELLED
        node.waitStatus = Node.CANCELLED;

        // 寫了一大堆,要做的就是把當前節點到pred節點中間的所有節點從等待隊列移除
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            // If successor needs signal, try to set pred's next-link
            // so it will get one. Otherwise wake it up to propagate.
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }

            node.next = node; // 使得當前節點不可達,讓GC
        }
    }

這個方法的作用就是移除等待隊列中的節點

shouldParkAfterFailedAcquire

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
            /*
             * 前一個節點已經取消,就跳過繼續往前找
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

這個方法主要的目的是當該線程嘗試獲取鎖失敗後,判斷該線程是否應該進入阻塞,如果前一個節點的狀態爲waitStatus就進入阻塞,如果不是就繼續嘗試獲取鎖。

parkAndCheckInterrupt

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();

阻塞線程,並且判斷線程是否中斷

acquireQueued

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node); //如果成功獲取鎖,就將當前的節點設置爲頭節點。看來等待隊列中的頭節點都是獲取到鎖的線程的節點
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 獲取失敗就進入阻塞
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

這個方法表示等待隊列中的節點會去嘗試獲取鎖,獲取到的成爲頭節點,獲取失敗的則線程繼續被阻塞。

doAcquireInterruptibly

private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        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())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

這個方法與acquireQueued很像,區別在於它獲取不到會拋出異常

doAcquireNanos

private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout; // 超時的時間點
        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;
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

這個方法獲取不到鎖會等待nanosTimeout納秒再去嘗試獲取,還是獲取不到就返回,不會阻塞。

doAcquireShared

private void doAcquireShared(int arg) {
    // 給等待隊列中加入共享節點
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                如果等待隊列的前一個節點就是頭結點,那麼就去獲取共享鎖
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    // 獲取成功就設置新的頭結點
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                // 阻塞等待
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

共享模式獲取鎖的時候在等待隊列中添加的節點都是一個單例Node.SHARED

doAcquireSharedInterruptibly

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

中斷模式的獲取共享鎖

doAcquireSharedNanos

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

超時模式的獲取共享鎖

acquire

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

這個方法的就是用來獲取獨佔鎖的,如果一次獲取失敗,那麼久不斷嘗試獲取直到獲取成功。

acquireInterruptibly

public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

這個方法的就是用來響應中斷地獲取獨佔鎖的,如果一次獲取失敗,那麼久不斷嘗試獲取直到獲取成功或者遇到中斷。

tryAcquireNanos

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }

這個方法獲取獨佔鎖,直到獲取成功或遇到中斷,或時間到

release

public final boolean release(int arg) {
    // 嘗試釋放鎖,釋放成功的話喚醒後面阻塞的線程
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

這個方法用於釋放鎖

acquireShared

public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

不可中斷的方式獲取共享鎖

acquireSharedInterruptibly

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

響應中斷的方式獲取共享鎖

tryAcquireSharedNanos

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

超時的方式獲取共享鎖

releaseShared

public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

釋放共享鎖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章