AQS原理剖析

AQS結構剖析

  • 雙向鏈表 + waitStatus的int值

鎖的結構:

  1. 實現Lock接口
  2. 組合AQS進行併發狀態控制

爲什麼使用雙向鏈表實現?

因爲鏈表移除和添加比較方便,只需要改動prev和next節點的指向即可,移除和添加都只需要操作一次,時間複雜度爲O(1)。如果使用數組去實現,隨着數據量的增加每次操作需要移動的次數也會更重

waitStatus的int值是什麼?有什麼用?

waitStatus

volatile int waitStatus AQS核心實現,等待狀態,它有幾種狀態值:CANCELLED、SIGNAL、CONDITION、PROPAGATE

static final class Node {
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
		/**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;

    	// AQS核心實現,等待狀態
        volatile int waitStatus;

        volatile Node prev;

        volatile Node next;

        volatile Thread thread;

        Node nextWaiter;

       
    }

**CANCELLED:**由鎖狀態變成取消狀態,這個時候就可以被gc回收了

SIGNAL: 插入名爲4的節點到3和2之間,然後將4節點的前繼節點也就是2的waitStatus改成SIGNAL狀態

其餘的節點相信大家直接看釋義就能明白了


源碼分析

AQS流程圖.png

參考流程圖,我們按照程序流程來分析源代碼

ReentrantLock

ReentrantLock支持公平鎖和非公平鎖,我們可以通過它的構造函數來控制選擇哪種鎖。默認無參構造是非公平鎖實現

ReentrantLock提供的公平鎖FairSync和非公平鎖NonfairSync都是繼承自AbstractQueuedSynchronizer就是AQS

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
    
    /**
     * 默認無參構造非公平鎖實現
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * 通過boolean fair控制選擇公平鎖和非公平鎖
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
        
        /**
         * lock加鎖方法,從這裏作爲入口開始分析
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();
        
        ......
        

lock

abstract void lock();是模板方法模式,由子類實現,在ReentrantLock中它的實現有公平鎖和非公平鎖兩者,這裏我們只有關注非公平鎖的實現

先來比較一下公平鎖和非公鎖的區別在哪?

重點看lock()方法

非公平鎖NonfairSync.lock()它一上來就先通過if (compareAndSetState(0, 1))cas去搶鎖

​ 如果搶鎖成功:

​ 則把當前線程,也就是自身,通過setExclusiveOwnerThread設置爲當前獨佔鎖的線程(佔用鎖的線程)

​ 如果搶鎖失敗:

​ 則走acquire(1)方法,繼續搶鎖,在失敗就通過enq加入阻塞隊列隊尾

公平鎖FairSync.lock(),則是直接調用acquire(1)方法,內部實現大致是先通過狀態判斷有無線程正在佔用,如果沒有也就是state == 0則繼續通過hasQueuedPredecessor判斷當前線程前面有沒有其它等待的線程,如果沒有在去搶鎖,如果有則返回false,通過enq加入阻塞隊列隊尾

獨佔鎖:同一時刻只有一個線程可以持有鎖,其它線程未獲取到鎖時,會被阻塞

/**
     * Sync object for non-fair locks
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

 	/**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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;
        }
    }

tryAcquire

我們繼續分析非公平鎖的實現

程序調用了acquire(1)之後,會先通過tryAcquire(1)去嘗試獲取鎖,重點看一下它的實現

它是先通過int c = getState()獲取鎖標記,判斷是否有鎖

​ 如果鎖狀態等於0,那說明無鎖

​ 則去通過cas搶鎖,搶鎖成功,則把自己設置爲獨佔鎖的線程

​ 如果鎖狀態不等於0,說明有鎖

​ 先走else if的判斷當前線程和獨佔鎖的線程是否爲同一線程,如果是,則直接拿到鎖,也就是重入鎖的特性,ReentrantLock就是重入獨佔鎖,拿到鎖之後繼續給state累加1,表示有鎖

​ 如果else if也判斷失敗,則返回false,tryAcquire嘗試獲取鎖失敗,這時走acquireQueued(addWaiter(Node.EXCLUSIVE), 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();
    // State: 鎖標記 0是無鎖、大於等於1是有鎖狀態()
    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;
}

加入隊列隊尾

在addWaiter中,能看到它是先創建了一個當前線程的node節點,然後獲取到了tail節點,也就是尾節點,如果tail節點存在,那麼則將當前線程創建的node節點的prev,也就是當前線程的前置節點指向現有的tail尾節點

然後通過cas搶鎖,搶鎖成功

​ 把自己設置爲尾節點,在把之前的尾節點的next指向現在的node節點,並返回node節點出去

搶鎖失敗

​ 則通過enq方法,自旋加入隊列。簡單的說enq之前的代碼是一種快速嘗試插入節點,加入隊列隊尾的方法

那麼爲什麼需要enq自旋入隊列呢?

​ 因爲在這裏是存在鎖競爭的,所以需要搶鎖,在操作

	/**
	 *	當前線程入隊列,並返回當前線程對應的node節點
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    private Node addWaiter(Node mode) {
        // 以給定模式構造節點。mode有兩種:EXCLUSVIE(獨佔)和SHARED(共享)
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 上面執行失敗,執行這裏自旋加入隊列,隊尾
        enq(node);
        return node;
    }
/**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    private Node enq(final Node node) {
        // CAS"自旋",直到成功加入隊尾
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                // 隊列爲空,創建一個空的標誌節點作爲head節點,並將tail也指向它
                // 創建第一個節點,頭尾都是自己
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else { // 正常流程,加入隊尾
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

判斷前驅節點釋放爲head

/**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true; // 標記是否成功拿到資源
        try {
            boolean interrupted = false; // 標記等待過程中是否被中斷過
            // 自旋
            for (;;) {
           		// 當前節點的前驅節點
                final Node p = node.predecessor();
                // 如果前驅節點是head,嘗試獲取資源(可能是head釋放完資源喚醒當前線程),當然也可能被interrupt)
                if (p == head && tryAcquire(arg)) {
                    // 競爭鎖成功
                    // 設置當前線程爲head節點
                    setHead(node);
                    // 出隊
                    p.next = null; // help GC
                    failed = false; // 成功獲取資源
                    return interrupted; // 返回等待過程中是否被中斷過
                }
                // park,掛起線程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

接下來我們看shouldParkAfterFailedAcquire方法

/**
 * Checks and updates status for a node that failed to acquire.
 * Returns true if thread should block. This is the main signal
 * control in all acquire loops.  Requires that pred == node.prev.
 *
 * @param pred node's predecessor holding status
 * @param node the node
 * @return {@code true} if thread should block
 */
// pred是前置節點,Node是當前節點
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 獲取前置節點的waitStatus
    int ws = pred.waitStatus;
    // SIGNAL的釋義,請看上面的waitStatus狀態值圖示
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) { // 取消調度,cancel了
        /*
         * 
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            // 看下面的圖示
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0); // 循環執行,直到waitStatus不大於0
        // 前置的next == 當前節點
        pred.next = node;
    } else {
        /*
         * 如果前驅正常,那就把前驅的狀態設置爲SIGNAL
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

if (ws > 0) 圖示

線程掛起阻塞

掛起線程

/**
     * Convenience method to park and then check if interrupted
     *
     * @return {@code true} if interrupted
     */
    private final boolean parkAndCheckInterrupt() {
        // 只有Unpark時才能解鎖
        LockSupport.park(this);
        return Thread.interrupted();
    }

unlock 喚醒後繼節點

那麼lock獲取鎖的流程已經完事了,現在就是解鎖的過程了

我們看看unLock();

public void unlock() {
        sync.release(1);
    }
public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                // 接觸被park的線程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
protected final boolean tryRelease(int releases) {
    		// 減去自旋增加的狀態值
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) { // 釋放
                free = true;
                setExclusiveOwnerThread(null);
            }
   			// 狀態最終需要設置回0
            setState(c);
            return free;
        }

真正的解鎖,解除被掛起的線程,喚醒後繼節點unparkSuccessor

/**
     * Wakes up node's successor, if one exists.
     *
     * @param node the node
     */
    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        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);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章