【線程】ReentrantLock 源碼剖析 (八)

我的原則:先會用再說,內部慢慢來


一、競爭鎖的場景

  1. 只有一個線程:
    1.1. threadA 首次直接成功 lock
    1.2. threadA 重入
  2. 存在多個線程競爭(假設2個線程,threadA + threadB: threadA 獲取到鎖,threadB 準備來搶 lock)
    2.1 threadA 還沒幹完活,threadB 阻塞
    2.2 threadA 已經幹完活來,threadB 直接拿到

在這裏插入圖片描述

二、代碼Hirerachy結構

在這裏插入圖片描述

架構代碼:

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    
    abstract static class Sync extends AbstractQueuedSynchronizer {}
    static final class NonfairSync extends Sync {}
    static final class FairSync extends Sync {}
}

三、相關對象展示

ReentrantLock lock = new ReentrantLock();
  1. ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;

    /**
     * ======================  抽象靜態內部類 sync  ======================  
     **/	
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
        abstract void lock();
        final boolean nonfairTryAcquire(int acquires){}
        protected final boolean tryRelease(int releases) {}
        protected final boolean isHeldExclusively() {}
        final ConditionObject newCondition() {}
        final Thread getOwner() {}
        final int getHoldCount() {}
        final boolean isLocked() {}
        private void readObject(java.io.ObjectInputStream s){}
    }

    /**
     * ======================  公平鎖與非公平鎖實現抽象靜態內部類 sync  ======================  
     **/
    static final class NonfairSync extends Sync {
        final void lock() {}
        protected final boolean tryAcquire(int acquires) {}
    }
    
    static final class FairSync extends Sync {
        final void lock() {}
        protected final boolean tryAcquire(int acquires) {}
    }

    /**
     * ======================  ReentrantLock 構造方法  ======================  
     **/
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    /**
     * ======================  ReentrantLock 下面的幾個方法都是基於 sync  ======================  
     **/
    public void lock() {
        sync.lock();
    }
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
    public void unlock() {
        sync.release(1);
    }
    public Condition newCondition() {
        return sync.newCondition();
    }
    public int getHoldCount() {
        return sync.getHoldCount();
    }
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }
    public boolean isLocked() {
        return sync.isLocked();
    }
    public final boolean isFair() {
        return sync instanceof FairSync;
    }
	protected Thread getOwner() {
        return sync.getOwner();
    }
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }
    public final int getQueueLength() {
        return sync.getQueueLength();
    }
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
    public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
    public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
    protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
}
  1. AbstractQueuedSynchronizer

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer implements java.io.Serializable {
    private transient volatile Node head;
    private transient volatile Node tail;
    private volatile long state;
    
    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;(Condition狀態。該節點處於條件隊列當中,該節點不會用作同步隊列直到設置狀態0用來傳輸時纔會移到同步隊列當中,並且加入對同步狀態的獲取)
        static final int PROPAGATE = -3;(迭代狀態。表示下一次共享式同步狀態獲取將會無條件地傳播下去)  
        volatile int waitStatus; // 等待狀態
        volatile Node prev; //前驅指針
        volatile Node next; // 後繼指針
        volatile Thread thread; //當前Node指向的thread
        Node nextWaiter; // 這個當 EXCLUSIVE 的時候,始終是null。它使用在 Condtion中

        final boolean isShared() {
            return nextWaiter == SHARED;
        }
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }
        Node() {    // Used to establish initial head or SHARED marker
        }
        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }
        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }
}
  1. AbstractOwnableSynchronizer
@Data
public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {
    	private transient Thread exclusiveOwnerThread;
}

四、根據場景剖析加鎖lock源碼(以非公平鎖爲例)

1.1 threadA 首次直接成功 lock

class NonfairSync extends Sync {
    final void lock() {
        //  必須在隊列爲空的情況下,才能執行這個操作。CAS 設置狀態 1
        if (compareAndSetState(0, 1))
            // 獲取到lock了,當前線程設置爲獨佔線程
            setExclusiveOwnerThread(Thread.currentThread());//AbstractOwnableSynchronizer#setExclusiveOwnerThread 
            /*
                ========  1.1. 首次直接成功 lock  ,程序跑到這裏,不往下走了 ========
             */
        else
            // 獲取不到鎖,走這裏
            acquire(1);
    }

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

在這裏插入圖片描述

1.2 threadA 重入

當前 lock.state >= 1,並且lock.currentThread = this, 走acquire(1)

  • 看下 acquire 方法:
void AbstractQueuedSynchronizer#acquire(int arg) {
    
	 // 下面 &&符號表示: tryAcquire 獲取不到鎖的時候,才繼續往下走
    if (!tryAcquire(arg) &&
	     /*
	      ======== 1.2. 重入成功 lock ,程序跑到這裏 tryAcquire 返回,不往下走了========
	     */
        // 這裏有倆方法: AbstractQueuedSynchronizer#acquireQueued 與 AbstractQueuedSynchronizer#addWaiter
          /*
           ======== 2.1. 同一個 lock  對象,thread1 拿住了這個lock,thread2 準備來搶奪 ========
          */
        acquireQueued(
            // Node EXCLUSIVE = null
            addWaiter(Node.EXCLUSIVE), arg)
        )
        selfInterrupt();
}
  • 看下 tryAcquire 方法:
protected boolean tryAcquire(int arg) {
          throw new UnsupportedOperationException();
}

AbstractQueuedSynchronizer#tryAcquire 必須後續實現,現在看 NonSync#tryAcquire

boolean NonfairSync#tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
  • 看下 nonfairTryAcquire 方法:
boolean Sync#nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    // 獲取 state,未設置,默認是 0
    int c = getState();
    // 1.2. 重入成功 c >=1 , 不走下面的if
    if (c == 0) {
        // CAS 設置狀態,跟上面的 NonfairSync#lock() 方法其實是有代碼冗餘
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            // 獲取到鎖,返回true,非重入。外面acquire就不往下走了
            return true;
        }
    }
    // 1.2. 重入成功 c >=1 , 跳過上面的if ,狀態+1
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        // 獲取到鎖,返回true,重入。外面acquire就不往下走了
        return true;
    }
    return false;
}

2.1 threadA 還沒幹完活,threadB 阻塞

acquireQueued(
    addWaiter(Node.EXCLUSIVE), arg)   // NodeEXCLUSIVE = null
)
  • 看 addWaiter 方法
Node AbstractQueuedSynchronizer#addWaiter(Node mode) {
    // Node.EXCLUSIVE = null
    Node node = new Node(Thread.currentThread(), mode);
    // 一開始 tail = null , 所以 pred 也是 null
    Node pred = tail;
   /*
        1. 等待隊列裏面一個Node都沒有,那麼 pred = null ,直接 enq(node)
        2. 等待隊列裏面最少有一個Node,那麼進入這個方法,NodeC 加入到隊列後面。
     */
    if (pred != null) {
        node.prev = pred;
        // 將新進來的 Node 接到隊列最後面
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            // 返回傳進來的節點
            return node;
        }
    }
    /*
      	enq 進隊列,
      	1. 隊列是空 :創建隊列,也就是整個隊列都沒Node的時候
      	2. 隊列不爲空:那麼跑到這一步的原因是上面setTail失敗了,也就是該node本該插入到隊列末端,但是沒有。因爲其他線程已經領先執行該操作了。爲了繼續往隊列的尾巴插入,調用了 enq方法。
      */
    enq(node);
    // 返回準備入隊列的 node
    return node;
}

AbstractQueuedSynchronizer.Node#Node(Thread thread, Node mode){     // Used by addWaiter
    this.nextWaiter = mode;
    this.thread = thread;
}

  • 看 enq 方法
Node AbstractQueuedSynchronizer#enq(final Node node) {
    for (;;) {
        /*
	        1. 隊列裏面是空: 
	            1.1 for 第一次循環  tail = null, 所以 t = null;
	            1.2 for 第二次循環 t = tail,tail 這個時候指向了 new Node(),所以 t 指向了 new Node()
	        2.  隊列不爲空,裏面只一個有 NodeB
	        	2.1 for CAS操作,把NodeC 插入到 NodeB 後面
         */
        Node t = tail; 
        if (t == null) { 
            // CAS 設置 head 指針,head 指向 new Node()
            if (compareAndSetHead(new Node()))
                // tail 也指向 new Node()
                tail = head;
        } else {
            /*
            	1. 隊列裏面是空: 
                	1.1 初始化head和tail後,第二次 for 的時候進入了。
                	1.2. 現在準備入隊列的 node 的前指針 prev 指向了 t,也就是 new Node()
                2. 隊列不爲空,只一個有 NodeB,那麼NodeC 插在NodeB的後面
             */
            node.prev = t;
            // CAS 設置 tail 指向了準備入隊列的 node,如果插入失敗就下個for繼續插入。
            if (compareAndSetTail(t, node)) {
                // t 的後指針 next 指向了 node
                t.next = node;
                // node前面的一個node返回,最前面是Node new,這個enq方法的返回值在 addWaiter 這一步驟沒被使用。
                return t;
            }
        }
    }
}

在這裏插入圖片描述

  • 看 acquireQueued 方法:

boolean AbstractQueuedSynchronizer#acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            /*
                AbstractQueuedSynchronizer.Node#prev
                1. 當 queue裏面只有 new node + 當前 nodeB 的時候,
                p = prev 指向了 new node ,和 head一樣,所以進入下面的方法。
             */
            final Node p = node.predecessor();
            /*  
                tryAcquire :
                    1. true 表示剛剛佔據lock的threadA,已經處理完畢釋放了lock ,this是threadB,現在有機會獲取鎖了。(debug的時候出現機率多,因爲停留太長時間了,threadA已經搞完事情了)
                    2. false 表示剛剛佔據lock的threadA現在還佔用着,所以走 shouldParkAfterFailedAcquire 
            */
            // 2.2 前驅p是head纔可以嘗試去 tryAcquire 獲取鎖,否則只能走下面去進隊列阻塞
            if (p == head && tryAcquire(arg)) {
                // head 指向nodeB,釋放 nodeB 的前驅指針 prev nodeNew
                setHead(node);
                
                p.next = null; // help GC p == head指向的臨時節點 new Node() 的後驅指針next指到 null
                failed = false;
                // return false,外面不往下走了
                return interrupted;
            }
            // 2.1 threadB 阻塞等待拿 lock
            // tryAcquire 返回 false ,第一次循環進入 shouldParkAfterFailedAcquire 返回了 false, pred指向的Node(new) ,waitStatus 從 0 變成了 1
            if (shouldParkAfterFailedAcquire(p, node) &&
                /*
                    tryAcquire 返回 false , pred指向的Node(new) 的 ,waitStatus現在是1了,shouldParkAfterFailedAcquire 返回了 true。
                    可以進入 parkAndCheckInterrupt ,這個方法會一直阻塞,直到拿到 lock,
                    LockSupport.park(this) 會阻塞線程,直到被叫醒: unlock的時候,LockSupport.unpark(s.thread);
                 */
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  • ===== 場景2.1 ===== 進入下面代碼段

  • 看下代碼段 (2.1 threadB 阻塞等待拿 lock 走到了這裏 )

if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;

boolean AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 前驅指針的狀態,此時應該是 new node(),waitStatus初始化默認是 0 
    int ws = pred.waitStatus;
       //狀態爲-1,表示後繼節點已經處於waiting等待狀態,等該節點釋放或取消,就會通知後繼節點
    if (ws == Node.SIGNAL) 
          return true;
      //如果狀態大於0--取消狀態,就跳過該節點循環往前找,找到一個非cancel狀態的節點
      if (ws > 0) {
          do {
              node.prev = pred = pred.prev;
          } while (pred.waitStatus > 0);
          //賦值pred的後繼節點爲node節點
          pred.next = node;
      } else {  //如果狀態小於0
          //必須是PROPAGATE或者0--表示無狀態,當是-2的時候,在condition queue隊列當中
          //通過CAS設置pred節點狀態爲signal
          compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
      }
      return false;
}
  • 看下 parkAndCheckInterrupt 方法:

能跑到這裏說明shouldParkAfterFailedAcquire返回了true,也就是 pred.waitStatus = Node.SIGNAL,也就是後繼節點需要被喚醒。successor’s thread needs unparking

boolean AbstractQueuedSynchronizer#parkAndCheckInterrupt() {
	//通過LockSupport工具阻塞當前線程
    LockSupport.park(this);
    return Thread.interrupted();
}

void LockSupport#park(java.lang.Object)(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(false, 0L); // 這個地方調用系統方式,直接阻塞停下來,等待 unpark
    setBlocker(t, null);
}

void LockSupport#setBlocker(Thread t, Object arg) {
    // Even though volatile, hotspot doesn't need a write barrier here.
    UNSAFE.putObject(t, parkBlockerOffset, arg);
}

  • 再來一個 NodeC,直接插入到NodeB 後面

在這裏插入圖片描述
自此, ===== 場景2.1 ===== 分析完畢。

2.2 threadA 已經幹完活來,threadB 直接拿到

threadB 進入的時候,首先進入阻塞。然後 threadA 跑完了,threadB tryAcquire 拿到鎖,返回

  1. ===== addWaiter===== 方法threadB沒拿到鎖,進入 acquireQueued方法
  2. ===== acquireQueued 方法 ===== 在內部的 ===== tryAcquire 方法 ===== 拿到了鎖
if (p == head && tryAcquire(arg)) {
       // head 指向nodeB,釋放 nodeB 的前驅指針 prev nodeNew
       setHead(node);
       
       p.next = null; // help GC p == head指向的臨時節點 new Node() 的後驅指針next指到 null
       failed = false;
       // return false,外面不往下走了
       return interrupted;
}
  • 看下 setHead 方法:

void AbstractQueuedSynchronizer#setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

若是NodeB 進來,那麼NodeB 的當前thread就變成了 null了,他變成頭節點了。因爲剛剛threadB 在tryAcquire 的時候已經獲取到鎖了。

在這裏插入圖片描述
在這裏插入圖片描述
自此, ===== 場景2.2 ===== 分析完畢。

五、unlock源碼(以非公平鎖爲例)

場景: threadB,threadC 正在阻塞,threadA幹完了,準備unlock
在這裏插入圖片描述

  • 看下代碼 lock.unlock
void ReentrantLock#unlock() {
    sync.release(1);
}

boolean AbstractQueuedSynchronizer#release(int arg) {
    if (tryRelease(arg)) {
        /*
         在沒有重入的情況下, tryRelease 返回 true,往下走
        */
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

boolean ReentrantLock.Sync#tryRelease(int releases) {
    int c = getState() - releases;
    // 只有獲取鎖纔有資格釋放鎖,否則拋異常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    /*
         在沒有重入的情況下, getState() == 0,c = 0
    */
    if (c == 0) {
        free = true;
        // 清空參數
        setExclusiveOwnerThread(null);
    }
    /*
         在沒有重入的情況下, c = 0
    */ 
    setState(c);
    return free;
}


  • 看下 AbstractQueuedSynchronizer#unparkSuccessor 方法

void AbstractQueuedSynchronizer#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.
     *
     * 1. unpark的thread正常的話是 head的下一個節點,目前就是 NodeNew的下一個節點。
     * 2. 但是如果Node的等待狀態被取消cancelled(一般是lock.lock(10),等待時間到了),或者是 node == null,
     * 那麼會從 tail 開始往前面找,找到 waitStatus <= 0 的節點。
     */
    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);
}
  1. threadA unlock方法之後,釋放鎖:
    在這裏插入圖片描述
  2. threadB 被喚醒,去拿鎖。
    ===== acquireQueued 方法 ===== 在內部的for 循環 拿到了鎖

在這裏插入圖片描述
在這裏插入圖片描述

========= 流程跑完 =========

六、番外篇

上一章節:【線程】ReentrantLock 實戰 (七)

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