jdk10-ReentrantLock重入鎖源碼分析(上)

1. 線(xiàn)程安全

多個(gè)線(xiàn)程對(duì)公共資源進(jìn)行非原子操作,就會(huì)存在線(xiàn)程安全問(wèn)題

  1. 多線(xiàn)程環(huán)境
  2. 多個(gè)線(xiàn)程共享一個(gè)資源
  3. 對(duì)資源進(jìn)行非原子性操作

2. Lock 接口介紹

Java中鎖的實(shí)現(xiàn)可以由synchronized關(guān)鍵字來(lái)完成,jdk1.5之后出現(xiàn)了一種新的方式來(lái)實(shí)現(xiàn)——Lock接口。

/**
 * 1.采用Lock,必須主動(dòng)去釋放鎖,并且在發(fā)生異常時(shí),不會(huì)自動(dòng)釋放鎖。 <br>
 * 2.因此一般來(lái)說(shuō),使用Lock必須在try{}catch{}塊中進(jìn)行,并且將釋放鎖的操作放在finally塊中進(jìn)行,以保證鎖一定被被釋放,防止死鎖的發(fā)生
 */
public interface Lock {
    /**
     * 獲取鎖。 如果鎖不可用,出于線(xiàn)程調(diào)度目的,將禁用當(dāng)前線(xiàn)程,并且在獲得鎖之前,該線(xiàn)程將一直處于休眠狀態(tài)。
     */
    void lock();
    /**
     * 可中斷獲取鎖,當(dāng)通過(guò)這個(gè)方法去獲取鎖時(shí),如果線(xiàn)程正在等待獲取鎖(休眠中),則這個(gè)線(xiàn)程能夠響應(yīng)中斷,即中斷線(xiàn)程的等待狀態(tài)<br>
     * 注意:當(dāng)一個(gè)線(xiàn)程獲取了鎖之后,是不會(huì)被interrupt()方法中斷的
     */
    void lockInterruptibly() throws InterruptedException;
    /**
     * 如果鎖可用,則獲取鎖,并立即返回值 true。如果鎖不可用,則此方法將立即返回值 false。
     */
    boolean tryLock();
    /**
     * 如果鎖可用,則此方法將立即返回值 true。如果鎖不可用,出于線(xiàn)程調(diào)度目的,將禁用當(dāng)前線(xiàn)程,并且在發(fā)生以下三種情況之一前,該線(xiàn)程將一直處于休眠狀態(tài):
     * <ul>
     * <li>1、鎖由當(dāng)前線(xiàn)程獲得;
     * <li>2、或者 其他某個(gè)線(xiàn)程中斷當(dāng)前線(xiàn)程,并且支持對(duì)鎖獲取的中斷;
     * <li>3、或者 已超過(guò)指定的等待時(shí)間
     * </ul>
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    /**
     * 釋放鎖。
     */
    void unlock();
    /**
     * 返回綁定到此 Lock 實(shí)例的新 Condition 實(shí)例。<br>
     * 在等待條件前,鎖必須由當(dāng)前線(xiàn)程保持。調(diào)用 Condition.await() 將在等待前以原子方式釋放鎖,并在等待返回前重新獲取鎖。
     */
    Condition newCondition();
}

3. ReentrantLock重入鎖

ReentrantLock,意思是“可重入鎖”,Lock接口有三個(gè)實(shí)現(xiàn)類(lèi)分別是ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock。后面兩個(gè)是內(nèi)部類(lèi)。

首先讓我們先簡(jiǎn)單的看看ReentrantLock類(lèi)中都有哪些方法,不喜歡可以直接跳過(guò)

/**
 * 除了實(shí)現(xiàn) Lock 接口,此類(lèi)還定義了 isLocked 和 getLockQueueLength 方法,以及一些相關(guān)的 protected
 * 訪(fǎng)問(wèn)方法,這些方法對(duì)檢測(cè)和監(jiān)視可能很有用。
 */
public class ReentrantLock implements Lock, java.io.Serializable { 
    private final Sync sync;
    /**
     * 為了將此類(lèi)用作同步器的基礎(chǔ),需要適當(dāng)?shù)刂匦露x以下方法。<br>
     * 
     * <li>{@link #tryAcquire}
     * <li>{@link #tryRelease}
     * <li>{@link #tryAcquireShared}
     * <li>{@link #tryReleaseShared}
     * <li>{@link #isHeldExclusively}
     * 
     * 這是通過(guò)使用 getState()、setState(int)或 compareAndSetState(int, int)
     * 方法來(lái)檢查或修改同步狀態(tài)來(lái)實(shí)現(xiàn)的
     */
    abstract static class Sync extends AbstractQueuedSynchronizer { 
        重寫(xiě)的父類(lèi)方法tryRelease
        protected final boolean tryRelease(int releases) {
            return false;
        }  
    }

    static final class NonfairSync extends Sync { 
        重寫(xiě)的父類(lèi)方法tryAcquire
        protected final boolean tryAcquire(int acquires) {
            return false;
        }
    }

    static final class FairSync extends Sync { 
        重寫(xiě)的父類(lèi)方法tryAcquire
        protected final boolean tryAcquire(int acquires) {
            return false;
        }
    }
 
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

以下是核心實(shí)現(xiàn)Lock接口的方法,我們可以看出,以下方法都是調(diào)用了內(nèi)部類(lèi)Sync

    /**
     * 獲取鎖,成功直接返回(計(jì)數(shù)加 1),失敗則線(xiàn)程阻塞
     */
    public void lock() {
        sync.acquire(1);
    } 
    /**
     * 中斷獲取鎖,成功直接返回(計(jì)數(shù)加 1)<br>
     * 失敗則當(dāng)前線(xiàn)程進(jìn)入休眠狀態(tài),直到以下兩個(gè)事件發(fā)生
     * <li>1. 當(dāng)前線(xiàn)程獲取鎖成功
     * <li>2. 當(dāng)前線(xiàn)程被其他線(xiàn)程中斷
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    } 
    /**
     * 嘗試非阻塞的獲取鎖,調(diào)用該方法立即返回,true表示獲取到鎖(計(jì)數(shù)加1)<br>
     * 同時(shí)這個(gè)鎖是不遵守公平設(shè)置的,如果想遵守公平設(shè)置獲取鎖可以使用 tryLock(0, TimeUnit.SECONDS)}
     */
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    } 
    /**
     * 超時(shí)獲取鎖,同時(shí)這個(gè)方法遵守公平設(shè)置 。 以下情況會(huì)返回:
     * <li>時(shí)間內(nèi)獲取到了鎖,
     * <li>時(shí)間內(nèi)被中斷,
     * <li>時(shí)間到了沒(méi)有獲取到鎖。
     */
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    } 
    /**
     * 釋放鎖,如果當(dāng)前線(xiàn)程是鎖持有者,則計(jì)數(shù)器減1,如果計(jì)數(shù)器為0則釋放鎖,如果當(dāng)前線(xiàn)程不是鎖持有者,拋出異常
     */
    public void unlock() {
        sync.release(1);
    } 
    /**
     * 返回用來(lái)與此 Lock 實(shí)例一起使用的 Condition 實(shí)例。
     */
    public Condition newCondition() {
        return sync.newCondition();
    } 
    
    /* 刪除了用于 監(jiān)控系統(tǒng),測(cè)試調(diào)試的方法*/
}

我們可以看到內(nèi)部所有的方法都是調(diào)用了內(nèi)部類(lèi) Sync,Sync繼承于AQS(AbstractQueuedSynchronizer ),同時(shí)Sync有兩個(gè)子類(lèi)分別是非公平NonfairSync,和公平FairSync

ReentrantLock結(jié)構(gòu)圖

5.1 模板模式

模板模式(Template Pattern)中,一個(gè)抽象類(lèi)公開(kāi)定義了執(zhí)行它的方法的方式/模板。它的子類(lèi)可以按需要重寫(xiě)方法實(shí)現(xiàn),但調(diào)用將以抽象類(lèi)中定義的方式進(jìn)行。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。

根據(jù)定義可以和明顯的看出來(lái) AQS使用了模板模式,

5.2 裝飾器模式

裝飾器模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類(lèi)型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類(lèi)的一個(gè)包裝。
這種模式創(chuàng)建了一個(gè)裝飾類(lèi),用來(lái)包裝原有的類(lèi),并在保持類(lèi)方法簽名完整性的前提下,提供了額外的功能。

根據(jù)裝飾器模式的介紹,感覺(jué) ReentrantLock 就像對(duì)Sync的包裝

5.3 策略模式

在策略模式(Strategy Pattern)中,一個(gè)類(lèi)的行為或其算法可以在運(yùn)行時(shí)更改。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式。

根據(jù)策略模式介紹,ReentrantLock的構(gòu)造器根據(jù)參數(shù)使用了簡(jiǎn)單的策略選擇

以上是對(duì)鎖類(lèi)型,Lock接口,ReentrantLock做了一個(gè)簡(jiǎn)單的介紹,以及個(gè)人對(duì)于設(shè)計(jì)模式的猜想吧,下面將以ReentrantLock.lock(), ReentrantLock.unlock()進(jìn)行一步一步的深入分析源碼,首先讓我們先簡(jiǎn)單的了解下AQS

6. AbstractQueuedSynchronizer(AQS 略講)

官方介紹:
為實(shí)現(xiàn)依賴(lài)于先進(jìn)先出 (FIFO) 等待隊(duì)列的阻塞鎖和相關(guān)同步器(信號(hào)量、事件,等等)提供一個(gè)框架。此類(lèi)的設(shè)計(jì)目標(biāo)是成為依靠單個(gè)原子 int 值來(lái)表示狀態(tài)的大多數(shù)同步器的一個(gè)有用基礎(chǔ)。子類(lèi)必須定義更改此狀態(tài)的受保護(hù)方法,并定義哪種狀態(tài)對(duì)于此對(duì)象意味著被獲取或被釋放。假定這些條件之后,此類(lèi)中的其他方法就可以實(shí)現(xiàn)所有排隊(duì)和阻塞機(jī)制。

  1. 先進(jìn)先出 (FIFO) 等待隊(duì)列 (通過(guò)字段head,tail來(lái)表示)
  2. 原子 int 值(volatile int state)
  3. 需要適當(dāng)?shù)刂匦露x以下方法(以下方法默認(rèn)實(shí)現(xiàn)都是直接拋出異常)
  • tryAcquire 試圖在獨(dú)占模式下獲取對(duì)象狀態(tài)。失敗將線(xiàn)程加入隊(duì)列
  • tryRelease 試圖設(shè)置狀態(tài)來(lái)反映獨(dú)占模式下的一個(gè)釋放。
  • tryAcquireShared 試圖在共享模式下獲取對(duì)象狀態(tài)
  • tryReleaseShared 試圖設(shè)置狀態(tài)來(lái)反映共享模式下的一個(gè)釋放。
  • isHeldExclusively 如果對(duì)于當(dāng)前線(xiàn)程,同步是以獨(dú)占方式進(jìn)行的,則返回 true

簡(jiǎn)單來(lái)說(shuō),AQS內(nèi)部一共有三個(gè)字段,head和tail構(gòu)造FIFO等待隊(duì)列,state表示狀態(tài),ReentrantLock的內(nèi)部類(lèi)Sync和子類(lèi)就是重寫(xiě)了tryAcquire 和 tryRelease 實(shí)現(xiàn)了鎖

    /**
     * 等待隊(duì)列的頭部,懶加載初始化,只能通過(guò)setHead修改,如果頭存在,它的等待狀態(tài)是 保證不被取消。
     */
    private transient volatile Node head;
    /**
     * 等待隊(duì)列的尾部,懶加載初始化,只有通過(guò)new 一個(gè)等待node才能修改
     */
    private transient volatile Node tail;
    /**
     * 同步狀態(tài)值
     */
    private volatile int state;

6.1 AQS內(nèi)部的node

之所以貼出來(lái)node的代碼,因?yàn)橛袔讉€(gè)狀態(tài)轉(zhuǎn)換,大家可以下面注意下

    static final class Node {
        /* 標(biāo)識(shí)節(jié)點(diǎn)當(dāng)前在共享模式下 */
        static final Node SHARED = new Node();
        /** 標(biāo)識(shí)節(jié)點(diǎn)當(dāng)前在獨(dú)占模式下 */
        static final Node EXCLUSIVE = null;

        /** 因?yàn)槌瑫r(shí)或者中斷,節(jié)點(diǎn)會(huì)被設(shè)置為取消狀態(tài),被取消的節(jié)點(diǎn)時(shí)不會(huì)參與到競(jìng)爭(zhēng)中的,他會(huì)一直保持取消狀態(tài)不會(huì)轉(zhuǎn)變?yōu)槠渌麪顟B(tài); */
        static final int CANCELLED = 1;
        /**
         * 后繼節(jié)點(diǎn)的線(xiàn)程處于等待狀態(tài),而當(dāng)前節(jié)點(diǎn)的線(xiàn)程如果釋放了同步狀態(tài)或者被取消,將會(huì)通知后繼節(jié)點(diǎn),使后繼節(jié)點(diǎn)的線(xiàn)程得以運(yùn)行,
         * 在AQS.shouldParkAfterFailedAcquire(pred,node)方法中會(huì)把前驅(qū)節(jié)點(diǎn)的狀態(tài)設(shè)置為此狀態(tài),表示隊(duì)列此節(jié)點(diǎn)后面有線(xiàn)程等待,可以理解為這個(gè)狀態(tài)是后面線(xiàn)程的狀態(tài) 
         */
        static final int SIGNAL = -1;
        /**
         * 節(jié)點(diǎn)在等待隊(duì)列中,節(jié)點(diǎn)線(xiàn)程等待在Condition上,當(dāng)其他線(xiàn)程對(duì)Condition調(diào)用了signal()后,改節(jié)點(diǎn)將會(huì)從等待隊(duì)列中轉(zhuǎn)移到同步隊(duì)列中,加入到同步狀態(tài)的獲取中
         */
        static final int CONDITION = -2;
        /**
         * 表示下一次共享式同步狀態(tài)獲取將會(huì)無(wú)條件地傳播下去 表示當(dāng)前場(chǎng)景下后續(xù)的acquireShared能夠得以執(zhí)行
         */
        static final int PROPAGATE = -3;
        /* 值為0,表示當(dāng)前節(jié)點(diǎn)在sync隊(duì)列中,等待著獲取鎖。 */
        volatile int waitStatus;

        /** 前驅(qū)節(jié)點(diǎn),比如當(dāng)前節(jié)點(diǎn)被取消,那就需要前驅(qū)節(jié)點(diǎn)和后繼節(jié)點(diǎn)來(lái)完成連接。 */
        volatile Node prev;

        /** 當(dāng)前節(jié)點(diǎn)點(diǎn)后繼節(jié)點(diǎn) */
        volatile Node next;

        /** 入隊(duì)列時(shí)的當(dāng)前線(xiàn)程。 */
        volatile Thread thread;

        /** 存儲(chǔ)condition隊(duì)列中的后繼節(jié)點(diǎn) */
        Node nextWaiter;

        /**
         * 共享模式返回true
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * 返回前驅(qū)節(jié)點(diǎn)
         */
        final Node predecessor() {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() { } 
        Node(Node nextWaiter) {
            this.nextWaiter = nextWaiter;
            THREAD.set(this, Thread.currentThread());
        } 
        Node(int waitStatus) {
            WAITSTATUS.set(this, waitStatus);
            THREAD.set(this, Thread.currentThread());
        }
    }

7. ReentrantLock.lock() (非公平)調(diào)用過(guò)程分析

7.1 線(xiàn)程1第一次調(diào)用

sync 是非公平鎖 NonfairSync extend Sync 的實(shí)例,核心的實(shí)現(xiàn)就是AQS.acquire(1)調(diào)用NonfairSync .tryAcquire(1),調(diào)用方法鏈

調(diào)用方法鏈
  1. Lock.lock()

直接調(diào)用內(nèi)部類(lèi)Sync的acquire(1),既AQS.acquire(1) ,acquire是sync從AQS繼承來(lái)的public final 方法

  1. AQS.acquire(1)

acquire直接調(diào)用了tryAcquire(1),此方法是需要子類(lèi)實(shí)現(xiàn),既NonfairSync.tryAcquire(1)

  1. NonfairSync.tryAcquire(1)

直接調(diào)用了父類(lèi)的Sync.nonfairTryAcquire(1)

  1. Sync.nonfairTryAcquire(1)
  1. 獲取同步狀態(tài)值volatile int state;
  2. state==0 cas比較替換
  3. 替換成功設(shè)置當(dāng)前線(xiàn)程返回true 獲取鎖成功
public class ReentrantLock implements Lock, java.io.Serializable {
    /** 可以看到此方法直接調(diào)用了 AQS的 public final方法 */
    public void lock() {
        sync.acquire(1);
    }
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
    // acquire 直接調(diào)用了 tryAcquire ,tryAcquire是抽象方法需要子類(lèi)NonfairSync 實(shí)現(xiàn), acquireQueued暫時(shí)忽略它, 我們假設(shè)第一次調(diào)用tryAcquire 返回true獲取到鎖
    public final void acquire(int arg) {
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
}

static final class NonfairSync extends Sync {
      // 直接調(diào)用父類(lèi)Sync的非公平nonfairTryAcquire
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

/**  */
abstract static class Sync extends AbstractQueuedSynchronizer {
        final boolean nonfairTryAcquire(int acquires) {
            1、獲取當(dāng)前線(xiàn)程
            final Thread current = Thread.currentThread();
            2、獲取 AQS volatile state 的狀態(tài)值
            int c = getState();
            3、狀態(tài)是否等于0  等于說(shuō)明此鎖暫時(shí)無(wú)線(xiàn)程占有 
            if (c == 0) {
                4、樂(lè)觀鎖設(shè)置狀態(tài) 比較替換狀態(tài)值 新的api利用了java9 的句柄替換了Unsafe實(shí)現(xiàn)
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            3.1、進(jìn)入這個(gè)else if分支,說(shuō)明是重入了,需要操作:state=state+1
            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;
        }
}

public abstract class AbstractQueuedSynchronizer {
// VarHandle mechanics java9利用句柄  提供了一系列標(biāo)準(zhǔn)的內(nèi)存屏障操作,
// 用于更加細(xì)粒度的控制內(nèi)存排序。在安全性、可用性、性能上都要優(yōu)于現(xiàn)有的API。
// VarHandle 可以與任何字段、數(shù)組元素或靜態(tài)變量關(guān)聯(lián),支持在不同訪(fǎng)問(wèn)模型下對(duì)這些類(lèi)型變量的訪(fǎng)問(wèn),
// 包括簡(jiǎn)單的 read/write 訪(fǎng)問(wèn),volatile 類(lèi)型的 read/write 訪(fǎng)問(wèn),和 CAS(compare-and-swap)等。
    private static final VarHandle STATE;

    protected final boolean compareAndSetState(int expect, int update) {
        return STATE.compareAndSet(this, expect, update);
    }
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class);
     }
}

7.2 線(xiàn)程2調(diào)用lock()

假如線(xiàn)程1和線(xiàn)程2同時(shí)調(diào)用lock()方法,線(xiàn)程1優(yōu)先獲取到鎖,線(xiàn)程2的執(zhí)行方法鏈如下


線(xiàn)程2執(zhí)行方法鏈
  1. Lock.lock() --> AQS.acquire(1) --> NonfairSync.tryAcquire(1) --> Sync.nonfairTryAcquire(1)

他們幾個(gè)方法依次調(diào)用,nonfairTryAcquire方法判斷 state!=0,不是當(dāng)前線(xiàn)程return false,然后AQS.acquire(1) 方繼續(xù)執(zhí)行acquireQueued(addWaiter(Node.EXCLUSIVE)

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

2、addWaiter(Node.EXCLUSIVE)

private Node addWaiter(Node mode) {
        Node node2 = new Node(mode);                     1、包裝當(dāng)前線(xiàn)程為node2(此處是我改為的node2方便理解)
        for (;;) { 
            Node oldTail = tail;                         2、獲取tail
            if (oldTail != null) { 
                node2.setPrevRelaxed(oldTail);             3、設(shè)置node2 的prev指向tail 
                if (compareAndSetTail(oldTail, node2)) {   4、樂(lè)觀鎖使tail指向node2 
                    oldTail.next = node2;                  5、老tail 的next指向node2              
                    return node2;
                }
            } else {
                initializeSyncQueue();  0、初始化AQS隊(duì)列如下圖
            }
        }
    }

    private final void initializeSyncQueue() {
        Node h;
        if (HEAD.compareAndSet(this, null, (h = new Node())))
            tail = h;
    }

  1. 包裝當(dāng)前線(xiàn)程為node(取個(gè)別名為node2)
  2. 獲取到tail節(jié)點(diǎn)
  3. 如果tail節(jié)點(diǎn)等于null(因?yàn)槭堑诙€(gè)線(xiàn)程所以此處tail節(jié)點(diǎn)等于null)初始化隊(duì)列
initializeSyncQueue();
  1. 循環(huán)再來(lái),如果tail節(jié)點(diǎn)不等于null(上面已經(jīng)初始化)
  • 4.1 設(shè)置當(dāng)前線(xiàn)程node2的prev指向tail(head的node1)
  • 4.2 樂(lè)觀鎖比較替換node2為tail(既tail指向node2)
  • 4.3 老tail(node1)的next指向當(dāng)前node2 返回當(dāng)前node2
image.png

3、AQS.acquireQueued(node2, 1)

  1. 獲取到當(dāng)前node2節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
  2. 判斷是head -- return true(此時(shí)線(xiàn)程2上面的情況前驅(qū)節(jié)點(diǎn)是head)
  3. tryAcquire(1) 嘗試去獲取鎖(假如這個(gè)時(shí)候,線(xiàn)程3來(lái)了獲取到了,或者線(xiàn)程1還沒(méi)執(zhí)行完畢)return false
  4. 執(zhí)行 shouldParkAfterFailedAcquire(p, node)
    final boolean acquireQueued(final Node node, int arg) {
        boolean interrupted = false;
        try {
            for (;;) {
                final Node p = node.predecessor(); 1. 獲取到當(dāng)前node2節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
                2. 判斷是head -- return true(此時(shí)線(xiàn)程2上面的情況前驅(qū)節(jié)點(diǎn)是head)
                3. tryAcquire(1) 嘗試去獲取鎖(假如這個(gè)時(shí)候,線(xiàn)程3來(lái)了獲取到了,或者線(xiàn)程1還沒(méi)執(zhí)行完畢)return false
                if (p == head && tryAcquire(arg)) {
** 假如線(xiàn)程2獲取到鎖,為啥設(shè)置自己為head,
** 因?yàn)榫€(xiàn)程3來(lái)了,他的前驅(qū)節(jié)點(diǎn)是線(xiàn)程2,所以線(xiàn)程2把自己設(shè)置為head,線(xiàn)程3才能進(jìn)入此方法
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                4. 執(zhí)行 shouldParkAfterFailedAcquire(p, node)
                if (shouldParkAfterFailedAcquire(p, node))
                    interrupted |= parkAndCheckInterrupt();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            if (interrupted)
                selfInterrupt();
            throw t;
        }
    }
  1. shouldParkAfterFailedAcquire 判斷此節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)狀態(tài),因?yàn)榍膀?qū)節(jié)點(diǎn)為head節(jié)點(diǎn),狀態(tài)為0,所以只會(huì)執(zhí)行設(shè)置 前驅(qū)節(jié)點(diǎn)狀態(tài)為 Node.SIGNAL 返回false
  2. 因?yàn)榉祷豧alse所以再次for循環(huán),假如再次沒(méi)有獲取到鎖,第二次進(jìn)入shouldParkAfterFailedAcquire ,因?yàn)閜rev的狀態(tài)剛剛被設(shè)置為signal,所以返回true
  3. 所以執(zhí)行 interrupted |= parkAndCheckInterrupt(); 阻塞當(dāng)前線(xiàn)程返回是否中斷
    (此處分析的是lock()方法只是返回,lockInterruptibly()會(huì)主動(dòng)拋出 InterruptedException)
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL) // 后繼節(jié)點(diǎn)的線(xiàn)程處于等待狀態(tài),而當(dāng)前節(jié)點(diǎn)的線(xiàn)程如果釋放了同步狀態(tài)或者被取消,將會(huì)通知后繼節(jié)點(diǎn),使后繼節(jié)點(diǎn)的線(xiàn)程得以運(yùn)行
            return true;
        if (ws > 0) { // 因?yàn)槌瑫r(shí)或者中斷,節(jié)點(diǎn)會(huì)被設(shè)置為取消狀態(tài)
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            *** 設(shè)置前驅(qū)節(jié)點(diǎn) 等待狀態(tài)
            pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
        }
        return false;
    }

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

7.3 線(xiàn)程3調(diào)用lock()

線(xiàn)程3執(zhí)行方法鏈

假如線(xiàn)程2處于等待狀態(tài),那么線(xiàn)程3會(huì)包裝成node3,在QAS.addWaiter(),會(huì)加入到Node2隊(duì)列后面,然后在AQS.acquireQueued(node,1),會(huì)設(shè)置前驅(qū)節(jié)點(diǎn)狀態(tài)為 Node.SIGNAL阻塞當(dāng)前線(xiàn)程,

線(xiàn)程3隊(duì)列

8. ReentrantLock.unlock() 調(diào)用過(guò)程分析

unlock方法鏈

1、 ReentrantLock.unlock()直接調(diào)用了 內(nèi)部類(lèi)Sync繼承來(lái)的方法AQS.release(1),

    public void unlock() {
        sync.release(1);
    }

2、AQS.release(1) 調(diào)用子類(lèi)實(shí)現(xiàn)的tryRelease(1)

    public final boolean release(int arg) {
1、釋放鎖成功 true
        if (tryRelease(arg)) {
            Node h = head;
2、我們知道線(xiàn)程2改變了head的狀態(tài)為Node.SIGNAL = -1 
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); 3、執(zhí)行unpark
            return true;
        }
        return false;
    }

3、Sync.tryRelease(1), 執(zhí)行完畢后回到上一步看注釋

  1. 獲取狀態(tài)減去1的值
  2. 如果不是當(dāng)前線(xiàn)程擁有者拋出異常
  3. 如果值等于0,表示釋放鎖
  4. 設(shè)置狀態(tài),不使用cas是因?yàn)楫?dāng)前方法必須是在當(dāng)前線(xiàn)程鎖定下調(diào)用的
        protected final boolean tryRelease(int releases) {
1、獲取狀態(tài)減去1的值
            int c = getState() - releases; 
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
2、如果值等于0,表示釋放鎖
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
3、設(shè)置狀態(tài),不使用cas是因?yàn)楫?dāng)前方法必須是在當(dāng)前線(xiàn)程鎖定下調(diào)用的
            setState(c);
            return free;
        }

4、unparkSuccessor(head)

    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.
         * 如果為負(fù)數(shù),說(shuō)明后繼有等待節(jié)點(diǎn),清理狀態(tài),為啥清理狀態(tài)目前還沒(méi)搞明白
         */
        int ws = node.waitStatus;
        if (ws < 0)
            node.compareAndSetWaitStatus(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.
         * 獲取當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn),如果==null,或者>0 節(jié)點(diǎn)取消了
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            從后往前遍歷,找到臨近當(dāng)前節(jié)點(diǎn)的正常狀態(tài)節(jié)點(diǎn)
            for (Node p = tail; p != node && p != null; p = p.prev)
                if (p.waitStatus <= 0)
                    s = p;
        }
        1、喚醒下一個(gè)節(jié)點(diǎn)
        if (s != null)
            LockSupport.unpark(s.thread);
    }

5、for循環(huán)為啥從后往前遍歷

一般不會(huì)出現(xiàn)倒敘全遍歷,因?yàn)槊看喂?jié)點(diǎn)的中斷取消都會(huì)清理一遍

  • 我們?cè)诨仡櫹?,addWaite(), 先設(shè)置當(dāng)前node的前驅(qū)節(jié)點(diǎn)為tail
  • 然后設(shè)置當(dāng)前節(jié)點(diǎn)為tail,

這樣在上面遍歷的時(shí)候,可以保證一旦節(jié)點(diǎn)加入到隊(duì)列,可以立馬獲取遍歷到他,如果是從前往后遍歷,這邊加入隊(duì)列的時(shí)候,先設(shè)置tail(已經(jīng)成功加入隊(duì)列),然后設(shè)置next,會(huì)出現(xiàn)并發(fā)問(wèn)題,可能遍歷不到最后一個(gè)新加入的節(jié)點(diǎn)

private Node addWaiter(Node mode) {
        Node node2 = new Node(mode);                     1、包裝當(dāng)前線(xiàn)程為node2(此處是我改為的node2方便理解)
        for (;;) { 
            Node oldTail = tail;                         2、獲取tail
            if (oldTail != null) { 
                node2.setPrevRelaxed(oldTail);             3、設(shè)置node2 的prev指向tail 
                if (compareAndSetTail(oldTail, node2)) {   4、樂(lè)觀鎖使tail指向node2 
                    oldTail.next = node2;                  5、老tail 的next指向node2              
                    return node2;
                }
            } else {
                initializeSyncQueue();  0、初始化AQS隊(duì)列如下圖
            }
        }
    }

6、如果線(xiàn)程2剛剛加入到隊(duì)列還沒(méi)調(diào)用LockSupport.park(this);,而線(xiàn)程1正好執(zhí)行完畢,優(yōu)先調(diào)用了線(xiàn)程2的unpark(),LockSupport.unpark(s.thread);

LockSupport 類(lèi)似于信號(hào)量Semaphore,如果在park之前調(diào)用了unpark,那么再調(diào)用park的時(shí)候不會(huì)阻塞,直接返回

9. 公平鎖

我們可以看到公平鎖在獲取鎖的過(guò)程中,比非公平鎖多了一個(gè)條件,hasQueuedPredecessors()判斷隊(duì)列中是否存在等待節(jié)點(diǎn),jdk10公平鎖,非公平鎖就這一點(diǎn)區(qū)別,和jdk8還是不太一樣的

    static final class FairSync extends Sync {
        @ReservedStackAccess
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
1、注意這里的 hasQueuedPredecessors()
                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;
        }
    }


    public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
 // h !=t 說(shuō)明隊(duì)列不為空
// (s = h.next) == null   不知道在什么情況下 h.next==null
// s.thread != Thread.currentThread()  如果有而且不能是當(dāng)前線(xiàn)程
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

9. 總結(jié)

本篇文章主要是介紹了,可重入鎖ReentrantLock的lock和unlock過(guò)程,至于ReentrantLock中的其他方法lockInterruptibly(),tryLock(),tryLock(long time, TimeUnit unit)就不做過(guò)多的啰嗦了,幾乎都是沒(méi)有什么差別的。下篇文章會(huì)對(duì)Condition做一個(gè)詳細(xì)的解讀

番外篇

1. Java鎖保證可見(jiàn)性的具體實(shí)現(xiàn)

Happens-before規(guī)則

  • 從JDK 5開(kāi)始,JSR-133定義了新的內(nèi)存模型,內(nèi)存模型描述了多線(xiàn)程代碼中的哪些行為是合法的,以及線(xiàn)程間如何通過(guò)內(nèi)存進(jìn)行交互。
  • 新的內(nèi)存模型語(yǔ)義在內(nèi)存操作(讀取字段,寫(xiě)入字段,加鎖,解鎖)和其他線(xiàn)程操作上創(chuàng)建了一些偏序規(guī)則,這些規(guī)則又叫作Happens-before規(guī)則。
  • 它的含義是當(dāng)一個(gè)動(dòng)作happens before另一個(gè)動(dòng)作,這意味著第一個(gè)動(dòng)作被保證在第二個(gè)動(dòng)作之前被執(zhí)行并且結(jié)果對(duì)其可見(jiàn)。我們利用Happens-before規(guī)則來(lái)解釋Java鎖到底如何保證了可見(jiàn)性。

Java內(nèi)存模型一共定義了八條Happens-before規(guī)則,和Java鎖相關(guān)的有以下兩條:

  1. 內(nèi)置鎖(synchronized)的釋放鎖操作發(fā)生在該鎖隨后的加鎖操作之前
  2. 一個(gè)volatile變量的寫(xiě)操作發(fā)生在這個(gè)volatile變量隨后的讀操作之前

2. 鎖釋放和獲取的內(nèi)存語(yǔ)義

  1. 當(dāng)線(xiàn)程釋放鎖時(shí),JMM會(huì)把該線(xiàn)程對(duì)應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存中
  2. 當(dāng)線(xiàn)程獲取鎖時(shí),JMM會(huì)把該線(xiàn)程對(duì)應(yīng)的本地內(nèi)存置為無(wú)效。從而使得被監(jiān)視器保護(hù)的臨界區(qū)代碼必須要從主內(nèi)存中去讀取共享變量

鎖釋放與volatile寫(xiě)有相同的內(nèi)存語(yǔ)義;鎖獲取與volatile讀有相同的內(nèi)存語(yǔ)義。

3. 下面對(duì)鎖釋放和鎖獲取的內(nèi)存語(yǔ)義做個(gè)總結(jié):

  • 線(xiàn)程A釋放一個(gè)鎖,實(shí)質(zhì)上是線(xiàn)程A向接下來(lái)將要獲取這個(gè)鎖的某個(gè)線(xiàn)程發(fā)出了(線(xiàn)程A對(duì)共享變量所做修改的)消息。
  • 線(xiàn)程B獲取一個(gè)鎖,實(shí)質(zhì)上是線(xiàn)程B接收了之前某個(gè)線(xiàn)程發(fā)出的(在釋放這個(gè)鎖之前對(duì)共享變量所做修改的)消息。
  • 線(xiàn)程A釋放鎖,隨后線(xiàn)程B獲取這個(gè)鎖,這個(gè)過(guò)程實(shí)質(zhì)上是線(xiàn)程A通過(guò)主內(nèi)存向線(xiàn)程B發(fā)送消息。

4. Lock與synchronized的區(qū)別:

  1. Lock是一個(gè)接口,是代碼層面的實(shí)現(xiàn);synchronized是關(guān)鍵字,是內(nèi)置的語(yǔ)言實(shí)現(xiàn)(JVM層面)。
  2. Lock是顯示地獲取釋放鎖,擴(kuò)展性更強(qiáng);synchronized是隱式地獲取釋放鎖,更簡(jiǎn)捷。
  3. Lock在發(fā)生異常時(shí),如果沒(méi)有主動(dòng)通過(guò)unlock()去釋放鎖,則很可能造成死鎖現(xiàn)象,因此使用Lock時(shí)需要在finally塊中釋放鎖;synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放線(xiàn)程占有的鎖,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生。
  4. Lock可以讓等待鎖的線(xiàn)程響應(yīng)中斷;而使用synchronized時(shí)等待鎖的線(xiàn)程會(huì)一直等待下去,不能響應(yīng)中斷;
  5. Lock可以嘗試非阻塞、可中斷、超時(shí)地獲取鎖;synchronized不可以。
  6. Lock可以知道是否成功獲取鎖;synchronized無(wú)法知道。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容