成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

圖解AQS原理之ReentrantLock詳解-非公平鎖

Clect / 2932人閱讀

摘要:內(nèi)部提供了兩種的實(shí)現(xiàn),一種公平模式,一種是非公平模式,如果沒有特別指定在構(gòu)造器中,默認(rèn)是非公平的模式,我們可以看一下無參的構(gòu)造函數(shù)。

概述

并發(fā)編程中,ReentrantLock的使用是比較多的,包括之前講的LinkedBlockingQueueArrayBlockQueue的內(nèi)部都是使用的ReentrantLock,談到它又不能的不說AQS,AQS的全稱是AbstractQueuedSynchronizer,這個(gè)類也是在java.util.concurrent.locks下面,提供了一個(gè)FIFO的隊(duì)列,可以用于構(gòu)建鎖的基礎(chǔ)框架,內(nèi)部通過原子變量state來表示鎖的狀態(tài),當(dāng)state大于0的時(shí)候表示鎖被占用,如果state等于0時(shí)表示沒有占用鎖,ReentrantLock是一個(gè)重入鎖,表現(xiàn)在state上,如果持有鎖的線程重復(fù)獲取鎖時(shí),它會(huì)將state狀態(tài)進(jìn)行遞增,也就是獲得一個(gè)信號(hào)量,當(dāng)釋放鎖時(shí),同時(shí)也是釋放了信號(hào)量,信號(hào)量跟隨減少,如果上一個(gè)線程還沒有完成任務(wù),則會(huì)進(jìn)行入隊(duì)等待操作。

本文分析內(nèi)容主要是針對(duì)jdk1.8版本

約束:文中圖片的ref-xxx代表引用地址

圖片中的內(nèi)容prve更正為prev,由于文章不是一天寫的所以有些圖片更正了有些沒有。

AQS主要字段
/**
 * 頭節(jié)點(diǎn)指針,通過setHead進(jìn)行修改
 */
private transient volatile Node head;

/**
 * 隊(duì)列的尾指針
 */
private transient volatile Node tail;

/**
 * 同步器狀態(tài)
 */
private volatile int state;
AQS需要子類實(shí)現(xiàn)的方法

AQS是提供了并發(fā)的框架,它內(nèi)部提供一種機(jī)制,它是基于模板方法的實(shí)現(xiàn),整個(gè)類中沒有任何一個(gè)abstract的抽象方法,取而代之的是,需要子類去實(shí)現(xiàn)的那些方法通過一個(gè)方法體拋出UnsupportedOperationException異常來讓子類知道,告知如果沒有實(shí)現(xiàn)模板的方法,則直接拋出異常。

方法名 方法描述
tryAcquire 以獨(dú)占模式嘗試獲取鎖,獨(dú)占模式下調(diào)用acquire,嘗試去設(shè)置state的值,如果設(shè)置成功則返回,如果設(shè)置失敗則將當(dāng)前線程加入到等待隊(duì)列,直到其他線程喚醒
tryRelease 嘗試獨(dú)占模式下釋放狀態(tài)
tryAcquireShared 嘗試在共享模式獲得鎖,共享模式下調(diào)用acquire,嘗試去設(shè)置state的值,如果設(shè)置成功則返回,如果設(shè)置失敗則將當(dāng)前線程加入到等待隊(duì)列,直到其他線程喚醒
tryReleaseShared 嘗試共享模式下釋放狀態(tài)
isHeldExclusively 是否是獨(dú)占模式,表示是否被當(dāng)前線程占用

AQS是基于FIFO隊(duì)列實(shí)現(xiàn)的,那么隊(duì)列的Node節(jié)點(diǎn)又是存放的什么呢?

Node字段信息
字段名 類型 默認(rèn)值 描述
SHARED Node new Node() 一個(gè)標(biāo)識(shí),指示節(jié)點(diǎn)使用共享模式等待
EXCLUSIVE Nodel Null 一個(gè)標(biāo)識(shí),指示節(jié)點(diǎn)使用獨(dú)占模式等待
CANCELLED int 1 節(jié)點(diǎn)因超時(shí)或被中斷而取消時(shí)設(shè)置狀態(tài)為取消狀態(tài)
SIGNAL int -1 當(dāng)前節(jié)點(diǎn)的后節(jié)點(diǎn)被park,當(dāng)前節(jié)點(diǎn)釋放時(shí),必須調(diào)用unpark通知后面節(jié)點(diǎn),當(dāng)后面節(jié)點(diǎn)競爭時(shí),會(huì)將前面節(jié)點(diǎn)更新為SIGNAL
CONDITION int -2 標(biāo)識(shí)當(dāng)前節(jié)點(diǎn)已經(jīng)處于等待中,通過條件進(jìn)行等待的狀態(tài)
PROPAGATE int -3 共享模式下釋放節(jié)點(diǎn)時(shí)設(shè)置的狀態(tài),被標(biāo)記為當(dāng)前狀態(tài)是表示無限傳播下去
0 int 不屬于上面的任何一種狀態(tài)
waitStatus int 0 等待狀態(tài),默認(rèn)初始化為0,表示正常同步等待,
pre Node Null 隊(duì)列中上一個(gè)節(jié)點(diǎn)
next Node Null 隊(duì)列中下一個(gè)節(jié)點(diǎn)
thread Thread Null 當(dāng)前Node操作的線程
nextWaiter Node Null 指向下一個(gè)處于阻塞的節(jié)點(diǎn)

通過上面的內(nèi)容我們可以看到waitStatus其實(shí)是有5個(gè)狀態(tài)的,雖然這里面0并不是什么字段,但是他是waitStatus狀態(tài)的一種,表示不是任何一種類型的字段,上面也講解了關(guān)于AQS中子類實(shí)現(xiàn)的方法,AQS提供了獨(dú)占模式和共享模式兩種,但是ReentrantLock實(shí)現(xiàn)的是獨(dú)占模式的方式,下面來通過源碼的方式解析ReentrantLock。

ReentrantLock源碼分析

首先在源碼分析之前我們先來看一下ReentrantLock的類的繼承關(guān)系,如下圖所示:

可以看到ReentrantLock繼承自Lock接口,它提供了一些獲取鎖和釋放鎖的方法,以及條件判斷的獲取的方法,通過實(shí)現(xiàn)它來進(jìn)行鎖的控制,它是顯示鎖,需要顯示指定起始位置和終止位置,Lock接口的方法介紹:

方法名稱 方法描述
lock 用來獲取鎖,如果鎖已被其他線程獲取,則進(jìn)行等待。
tryLock 表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲?。?,則返回false,也就說這個(gè)方法無論如何都會(huì)立即返回。在拿不到鎖時(shí)不會(huì)一直在那等待
tryLock(long time, TimeUnit unit) 和tryLock()類似,區(qū)別在于它在拿不到鎖時(shí)會(huì)等待一定的時(shí)間,在時(shí)間期限之內(nèi)如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間內(nèi)拿到了鎖,則返回true
lockInterruptibly 獲取鎖,如果獲取鎖失敗則進(jìn)行等到,如果等待的線程被中斷會(huì)相應(yīng)中斷信息。
unlock 釋放鎖的操作
newCondition 獲取Condition對(duì)象,該組件和當(dāng)前的鎖綁定,當(dāng)前線程只有獲得了鎖,才能調(diào)用該組件wait()方法,而調(diào)用后,當(dāng)前線程釋放鎖。

ReentrantLock也實(shí)現(xiàn)了上面接口的內(nèi)容,前面講解了很多理論行的內(nèi)容,接下來我們以一個(gè)簡單的例子來進(jìn)行探討

public class ReentrantLockDemo {
    public static void main(String[] args) throws Exception {
        AddDemo runnalbeDemo = new AddDemo();
        Thread thread = new Thread(runnalbeDemo::add);
        thread.start();
        Thread thread1 = new Thread(runnalbeDemo::add);
        thread1.start();
          Thread.sleep(1000);
        System.out.println(runnalbeDemo.getCount());
    }
    
    private static class AddDemo {
        private final AtomicInteger count = new AtomicInteger();
        private final ReentrantLock reentrantLock = new ReentrantLock();

        private void add() {
            try {
                reentrantLock.lock();
                count.getAndIncrement();
            } finally {
//                reentrantLock.unlock();
            }
        }

        int getCount() {
            return count.get();
        }
    }
}

首先聲明內(nèi)部類AddDemo,AddDemo的主要作用是將原子變量count進(jìn)行遞增的操作

AddDemo內(nèi)部聲明了ReentrantLock對(duì)象進(jìn)行同步操作

AddDemo的add方法,進(jìn)行遞增操作,細(xì)心地同學(xué)發(fā)現(xiàn),使用了lock方法獲取鎖,但是沒有釋放鎖,這里面沒有釋放鎖可以更讓我們清晰的分析內(nèi)部結(jié)構(gòu)的變化。

主線程開啟了兩個(gè)線程進(jìn)行同步進(jìn)行遞增的操作,最后讓線程休眠一會(huì)輸出累加的最后結(jié)果。

ReentrantLock內(nèi)部提供了兩種AQS的實(shí)現(xiàn),一種公平模式,一種是非公平模式,如果沒有特別指定在構(gòu)造器中,默認(rèn)是非公平的模式,我們可以看一下無參的構(gòu)造函數(shù)。

public ReentrantLock() {
    sync = new NonfairSync();
}

當(dāng)調(diào)用有參構(gòu)造函數(shù)時(shí),指定使用哪種模式來進(jìn)行操作,參數(shù)為布爾類型,如果指定為false的話代表非公平模式,如果指定為true的話代表的是公平模式,如下所示:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

我們使用的是非公平模式,后面再來進(jìn)行分析公平模式,上面也講到了分為兩種模式,這兩種模式為FairSyncNonfairSync兩個(gè)內(nèi)部靜態(tài)類不可變類,不能被繼承和實(shí)例化,這兩個(gè)類是我們今天分析的重點(diǎn),為什么說是重點(diǎn)呢,這里講的內(nèi)容是有關(guān)于AQS的,而FairSyncNonfairSync實(shí)現(xiàn)了抽象內(nèi)部類Sync,Sync實(shí)現(xiàn)了AbstractQueuedSynchronizer這個(gè)類,這個(gè)類就是我們說的AQS也是主要同步操作的類,下面我們來看一下公平模式和非公平模式下類的繼承關(guān)系,如下圖所示:

非公平模式:

公平模式:


通過上面兩個(gè)繼承關(guān)系UML來看其實(shí)無差別,差別在于內(nèi)部實(shí)現(xiàn)的原理不一樣,回到上面例子中使用的是非公平模式,那先以非公平模式來進(jìn)行分析,

假設(shè)第一個(gè)線程啟動(dòng)調(diào)用AddDemo的add方法時(shí),首先執(zhí)行的事reentrantLock.lock()方法,這個(gè)lock方法調(diào)用了sync.lock(),sync就是我們上面提到的兩種模式的對(duì)象,來看一下源碼內(nèi)容:

public void lock() {
    sync.lock();
}

內(nèi)部調(diào)用了sync.lock(),其實(shí)是調(diào)用了NonfairSync對(duì)象的lock方法,也就是下面的方法內(nèi)容。

/**
 * 非公平模式鎖
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * 執(zhí)行鎖動(dòng)作,先進(jìn)行修改狀態(tài),如果鎖被占用則進(jìn)行請(qǐng)求申請(qǐng)鎖,申請(qǐng)鎖失敗則將線程放到隊(duì)列中
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    // 繼承自AQS的tryAcquire方法,嘗試獲取鎖操作,這個(gè)方法會(huì)被AQS的acquire調(diào)用
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

我們看到lock方法首先先對(duì)state狀態(tài)進(jìn)行修改操作,如果鎖沒有被占用則獲取鎖,并設(shè)置當(dāng)前線程獨(dú)占鎖資源,如果嘗試獲取鎖失敗了,則進(jìn)行acqurie方法的調(diào)用,例子中第一個(gè)線程當(dāng)嘗試獲取鎖是內(nèi)部state狀態(tài)為0,進(jìn)行修改操作的時(shí)候,發(fā)現(xiàn)鎖并沒有被占用,則獲得鎖,此時(shí)我們來看一下內(nèi)部變化的情況,如下圖所示:

此時(shí)只是將state的狀態(tài)更新為1,表示鎖已經(jīng)被占用了,獨(dú)占鎖資源的線程是Thread0,也就是exclusiveOwnerThread的內(nèi)容,頭節(jié)點(diǎn)和尾節(jié)點(diǎn)都沒有被初始化,當(dāng)?shù)诙€(gè)線程嘗試去獲取鎖的時(shí)候,發(fā)現(xiàn)鎖已經(jīng)被占用了,因?yàn)樯弦粋€(gè)線程并沒有釋放鎖,所以第二線程直接獲取鎖時(shí)獲取失敗則進(jìn)入到acquire方法中,這個(gè)方法是AbstractQueuedSynchronizer中的方法acquire,先來看一下具體的實(shí)現(xiàn)源碼如下所示:

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

我個(gè)人理解acquire方法不間斷的嘗試獲取鎖,如果鎖沒有獲取到則現(xiàn)將節(jié)點(diǎn)加入到隊(duì)列中,并將當(dāng)前線程設(shè)置為獨(dú)占鎖資源,也就是獨(dú)占了鎖的意思,別的線程不能擁有鎖,然后如果當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)是頭節(jié)點(diǎn)話,再去嘗試爭搶鎖,則設(shè)置當(dāng)前節(jié)點(diǎn)為頭節(jié)點(diǎn),并將原頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置為null,幫助GC回收它,如果不是頭節(jié)點(diǎn)或爭搶鎖不成功,則會(huì)現(xiàn)將前面節(jié)點(diǎn)的狀態(tài)設(shè)置直到設(shè)置為SIGNAL為止,代表下面有節(jié)點(diǎn)被等待了等待上一個(gè)線程發(fā)來的信號(hào),然后就掛起當(dāng)前線程。

我們接下來慢慢一步一步的分析,我們先來看一下NonfairSync中的tryAcquire,如下所示:

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

它調(diào)用的是他的父類方法,也就是ReentrantLockSync中的nonfairTryAcquire方法,這個(gè)方法主要就是去申請(qǐng)鎖的操作,來看一下具體源碼:

final boolean nonfairTryAcquire(int acquires) {        //首先是一個(gè)被final修飾的方法
    final Thread current = Thread.currentThread();    //獲取當(dāng)前線程
    int c = getState();                                //獲取state的狀態(tài)值
    if (c == 0) {                                    //如果狀態(tài)等于0代表線程沒有被占用
        if (compareAndSetState(0, acquires)) {        //cas修改state值
            setExclusiveOwnerThread(current);        //設(shè)置當(dāng)前線程為獨(dú)占模式
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {//如果state狀態(tài)不等于0則先判斷是否是當(dāng)前線程占用鎖,如果是則進(jìn)行下面的流程。
        int nextc = c + acquires;                    //這個(gè)地方就說明重入鎖的原理,如果擁有鎖的是當(dāng)前線程,則每次獲取鎖state值都會(huì)跟隨遞增
        if (nextc < 0) // overflow                    //溢出了
            throw new Error("Maximum lock count exceeded");
        setState(nextc);                            //直接設(shè)置state值就可以不需要CAS
        return true;
    }
    return false;                                    //都不是就返回false
}

通過源碼我們可以看到其實(shí)他是有三種操作邏輯:

如果state為0,則代表鎖沒有被占用,嘗試去修改state狀態(tài),并且將當(dāng)前線程設(shè)置為獨(dú)占鎖資源,表示獲得鎖成功

如果state大于0并且擁有鎖的線程和當(dāng)前申請(qǐng)鎖的線程一致,則代表重入了鎖,state值會(huì)進(jìn)行遞增,表示獲得鎖成功

如果state大于0并且擁有鎖的線程和當(dāng)前申請(qǐng)鎖的線程不一致則直接返回false,代表申請(qǐng)鎖失敗

當(dāng)?shù)诙€(gè)線程去爭搶鎖的時(shí)候,state值已經(jīng)設(shè)置為1了也就是已經(jīng)被第一個(gè)線程占用了鎖,所以這里它會(huì)返回false,而通過acquire方法內(nèi)容可以看到if語句中是!tryAcquire(arg),也就是!false=ture,它會(huì)進(jìn)行acquireQueued(addWaiter(Node.EXCLUSIVE), arg))方法,這個(gè)方法里面又有一個(gè)addWaiter方法,從方法語義上能看到是添加等待隊(duì)列的操作,方法的參數(shù)代表的是模式,Node.EXCLUSIVE表示的是在獨(dú)占模式下等待,我們先來看一下addWaiter里面是如何進(jìn)行操作,如下所示:

private Node addWaiter(Node mode) {
      //首先生成當(dāng)前線程擁有的節(jié)點(diǎn)
    Node node = new Node(Thread.currentThread(), mode);
    // 下面的內(nèi)容是嘗試快速進(jìn)行插入末尾的操作,在沒有其他線程同時(shí)操作的情況
    Node pred = tail;                                            //獲取尾節(jié)點(diǎn)
    if (pred != null) {                                            //尾節(jié)點(diǎn)不為空,代表隊(duì)列不為空
        node.prev = pred;                                        //尾節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)
        if (compareAndSetTail(pred, node)) {                    //修改尾節(jié)點(diǎn)為當(dāng)前節(jié)點(diǎn)
            pred.next = node;                                    //原尾節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)
            return node;                                        //返回node節(jié)點(diǎn)
        }
    }
    enq(node);                                                    //如果前面入隊(duì)失敗,這里進(jìn)行循環(huán)入隊(duì)操作,直到入隊(duì)成功
    return node;
}

前面代碼中可以看到,它有一個(gè)快速入隊(duì)的操作,如果快速入隊(duì)失敗則進(jìn)行死循環(huán)進(jìn)行入隊(duì)操作,當(dāng)然我們上面例子中發(fā)現(xiàn)隊(duì)列其實(shí)是為空的,也就是pred==null,不能進(jìn)行快速入隊(duì)操作,則進(jìn)入到enq進(jìn)行入隊(duì)操作,下面看一下enq方法實(shí)現(xiàn),如下所示:

private Node enq(final Node node) {
    for (;;) {                                                    //死循環(huán)進(jìn)行入隊(duì)操作,直到入隊(duì)成功
        Node t = tail;                                            //獲取尾節(jié)點(diǎn)
        if (t == null) { // Must initialize                        //判斷尾節(jié)點(diǎn)為空,則必須先進(jìn)行初始化
            if (compareAndSetHead(new Node()))                  //生成一個(gè)Node,并將當(dāng)前Node作為頭節(jié)點(diǎn)
                tail = head;                                    //head和tail同時(shí)指向上面Node節(jié)點(diǎn)
        } else {                                                            
            node.prev = t;                                        //設(shè)置入隊(duì)的當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)設(shè)置為尾節(jié)點(diǎn)    
            if (compareAndSetTail(t, node)) {                    //將當(dāng)前節(jié)點(diǎn)設(shè)置為尾節(jié)點(diǎn)
                t.next = node;                                    //修改原有尾節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)為當(dāng)前節(jié)點(diǎn)
                return t;                                        //返回最新的節(jié)點(diǎn)
            }
        }
    }
}

通過上面入隊(duì)操作,可以清晰的了解入隊(duì)操作其實(shí)就是Node節(jié)點(diǎn)的prev節(jié)點(diǎn)和next節(jié)點(diǎn)之前的引用,運(yùn)行到這里我們應(yīng)該能看到入隊(duì)的狀態(tài)了,如下圖所示:

如上圖可以清晰的看到,此時(shí)擁有鎖的線程是Thread0,而當(dāng)前線程是Threa1,頭節(jié)點(diǎn)為初始化的節(jié)點(diǎn),Ref-707引用地址所在的Node節(jié)點(diǎn)操作當(dāng)前操作的節(jié)點(diǎn)信息,入隊(duì)操作后并沒有完成,而是繼續(xù)往下進(jìn)行,此時(shí)則進(jìn)行acquireQueued這個(gè)方法,這個(gè)方法是不間斷的去獲取已經(jīng)入隊(duì)隊(duì)列中的前節(jié)點(diǎn)的狀態(tài),如果前節(jié)點(diǎn)的狀態(tài)為大于0,則代表當(dāng)前節(jié)點(diǎn)被取消了,會(huì)一直往前面的節(jié)點(diǎn)進(jìn)行查找,如果節(jié)點(diǎn)狀態(tài)小于0并且不等于SIGNAL則將其設(shè)置為SIGNAL狀態(tài),設(shè)置成功后將當(dāng)前線程掛起,掛起線程后也有可能會(huì)反復(fù)喚醒掛起操作,原因后面會(huì)講到。

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;                            //取消節(jié)點(diǎn)標(biāo)志位
    try {
        boolean interrupted = false;                //中斷標(biāo)志位
        for (;;) {
            final Node p = node.predecessor();        //獲取前節(jié)點(diǎn)
            if (p == head && tryAcquire(arg)) {        //這里的邏輯是如果前節(jié)點(diǎn)為頭結(jié)點(diǎn)并且獲取到鎖則進(jìn)行頭結(jié)點(diǎn)變換
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&    //設(shè)置waitStatus狀態(tài)
                parkAndCheckInterrupt())                    //掛起線程
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);                            //取消操作
    }
}

前面的源碼可以看到它在acquireQueued中對(duì)已經(jīng)入隊(duì)的節(jié)點(diǎn)進(jìn)行嘗試鎖的獲取,如果鎖獲得就修改頭節(jié)點(diǎn)的指針,如果不是頭節(jié)點(diǎn)或者爭搶鎖失敗時(shí),此時(shí)會(huì)進(jìn)入到shouldParkAfterFailedAcquire方法,這個(gè)方法是獲取不到鎖時(shí)需要停止繼續(xù)無限期等待鎖,其實(shí)就是內(nèi)部的操作邏輯也很簡單,就是如果前節(jié)點(diǎn)狀態(tài)為0時(shí),需要將前節(jié)點(diǎn)修改為SIGNAL,如果前節(jié)點(diǎn)大于0則代表前節(jié)點(diǎn)已經(jīng)被取消了,應(yīng)該移除隊(duì)列,并將前前節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn),一直循環(huán)直到前節(jié)點(diǎn)狀態(tài)修改為SIGNAL或者前節(jié)點(diǎn)被釋放鎖,當(dāng)前節(jié)點(diǎn)獲取到鎖停止循環(huán)。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * 此節(jié)點(diǎn)已經(jīng)設(shè)置了狀態(tài),要求對(duì)當(dāng)前節(jié)點(diǎn)進(jìn)行掛起操作
         */
        return true;
    if (ws > 0) {
        /*
         * 如果前節(jié)點(diǎn)被取消,則將取消節(jié)點(diǎn)移除隊(duì)列操作
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus=0或者PROPAGATE時(shí),表示當(dāng)前節(jié)點(diǎn)還沒有被掛起停止,需要等待信號(hào)來通知節(jié)點(diǎn)停止操作。
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

上面的方法其實(shí)很容易理解就是等待掛起信號(hào),如果前節(jié)點(diǎn)的狀態(tài)為0或PROPAGATE則將前節(jié)點(diǎn)修改為SIGNAL,則代表后面前節(jié)點(diǎn)釋放鎖后會(huì)通知下一個(gè)節(jié)點(diǎn),也就是說喚醒下一個(gè)可以喚醒的節(jié)點(diǎn)繼續(xù)爭搶所資源,如果前節(jié)點(diǎn)被取消了那就繼續(xù)往前尋找不是被取消的節(jié)點(diǎn),這里不會(huì)找到前節(jié)點(diǎn)為null的情況,因?yàn)樗J(rèn)會(huì)有一個(gè)空的頭結(jié)點(diǎn),也就是上圖內(nèi)容,此時(shí)的隊(duì)列狀態(tài)是如何的我們看一下,這里它會(huì)進(jìn)來兩次,以為我們上圖可以看到當(dāng)前節(jié)點(diǎn)前節(jié)點(diǎn)是Ref-724此時(shí)waitStatus=0,他需要先將狀態(tài)更改為SIGNAL也就是運(yùn)行最有一個(gè)else語句,此時(shí)又會(huì)回到外面的for循環(huán)中,由于方法返回的是false則不會(huì)運(yùn)行parkAndCheckInterrupt方法,而是又循環(huán)了一次,此時(shí)發(fā)現(xiàn)當(dāng)前節(jié)點(diǎn)爭搶鎖又失敗了,然后此時(shí)隊(duì)列的狀態(tài)如下圖所示:

再次進(jìn)入到方法之后發(fā)現(xiàn)前驅(qū)節(jié)點(diǎn)的waitStatus=-1,表示當(dāng)前節(jié)點(diǎn)需要進(jìn)行掛起等到,此時(shí)返回的結(jié)果是true,則會(huì)運(yùn)行parkAndCheckInterrupt方法,這個(gè)方法很簡單就是將當(dāng)前線程進(jìn)行掛起操作,如下所示:

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);                        //掛起線程
    return Thread.interrupted();                //判斷是否被中斷,獲取中斷標(biāo)識(shí)
}

park掛起線程并且響應(yīng)中斷信息,其實(shí)我們從這里就能發(fā)現(xiàn)一個(gè)問題,Thread.interrupted方法是用來獲取是否被中斷的標(biāo)志,如果被中斷則返回true,如果沒有被中斷則返回false,當(dāng)當(dāng)前節(jié)點(diǎn)被中斷后,其實(shí)就會(huì)返回true,返回true這里并沒有結(jié)束,而是跳到調(diào)用地方,也就是acquireQueued方法內(nèi)部:

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

以一個(gè)案例來進(jìn)行分析:

public class ReentrantLockDemo {
    public static void main(String[] args) throws Exception {
        AddDemo runnalbeDemo = new AddDemo();
        Thread thread = new Thread(runnalbeDemo::add);
        thread.start();
        Thread thread1 = new Thread(runnalbeDemo::add);
        thread1.start();
        Thread thread2 = new Thread(runnalbeDemo::add);
        thread2.start();

        Thread.sleep(10000);
        thread1.interrupt();
        System.out.println(runnalbeDemo.getCount());
    }

    private static class AddDemo {
        private final AtomicInteger count = new AtomicInteger();
        private final ReentrantLock reentrantLock = new ReentrantLock();
        private final Condition condition = reentrantLock.newCondition();

        private void add() {
            try {
                reentrantLock.lock();
                count.getAndIncrement();
            } finally {
//                reentrantLock.unlock();
            }
        }

        int getCount() {
            return count.get();
        }
    }
}

通過上面的例子可以發(fā)現(xiàn),thread1調(diào)用中斷方法interrupt(),當(dāng)調(diào)用第一次方法的時(shí)候,它會(huì)進(jìn)入到parkAndCheckInterrupt方法,然后線程響應(yīng)中斷,最后返回true,最后返回到acquireQueued方法內(nèi)部,整個(gè)if語句為true,則開始設(shè)置interrupted=true,僅僅是設(shè)置了等于true,但是這離還會(huì)進(jìn)入下一輪的循環(huán),假如說上次的線程沒有完成任務(wù),則沒有獲取到鎖,還是會(huì)進(jìn)入到shouldParkAfterFailedAcquire由于已經(jīng)修改了上一個(gè)節(jié)點(diǎn)的waitStatus=-1,直接返回true,然后再進(jìn)入到parkAndCheckInterrupt又被掛起線程,但是如果上步驟操作他正搶到鎖,則會(huì)返回ture,外面也會(huì)清除中斷標(biāo)志位,從這里可以清楚地看到acquire方法是一個(gè)不間斷獲得鎖的操作,可能重復(fù)阻塞和解除阻塞操作。

上面阻塞隊(duì)列的內(nèi)容已經(jīng)講完了,接下來我們看一下unlock都為我們做了什么工作:

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

我們可以看到他直接調(diào)用了獨(dú)占模式的release方法,看一下具體源碼:

public final boolean release(int arg) {
    if (tryRelease(arg)) {                    //調(diào)用ReentrantLock中的Sync里面的tryRelease方法
        Node h = head;                        //獲取頭節(jié)點(diǎn)
        if (h != null && h.waitStatus != 0)    //頭節(jié)點(diǎn)不為空且狀態(tài)不為0時(shí)進(jìn)行unpark方法
            unparkSuccessor(h);                //喚醒下一個(gè)未被取消的節(jié)點(diǎn)
        return true;
    }
    return false;
}

release方法,首先先進(jìn)行嘗試去釋放鎖,如果釋放鎖仍然被占用則直接返回false,如果嘗試釋放鎖時(shí),發(fā)現(xiàn)鎖已經(jīng)釋放,當(dāng)前線程不在占用鎖資源時(shí),則會(huì)進(jìn)入的下面進(jìn)行一些列操作后返回true,接下來我們先來看一下ReentrantLockSync下的tryRelease方法,如下所示:

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;                                //獲取state狀態(tài),標(biāo)志信息減少1
    if (Thread.currentThread() != getExclusiveOwnerThread())    //線程不一致拋出異常
        throw new IllegalMonitorStateException();
    boolean free = false;                                                                                                                        
    if (c == 0) {                                                //是否已經(jīng)釋放鎖,start=0代表已經(jīng)釋放鎖                                                
        free = true;                                            //將標(biāo)志free設(shè)置為true                                                                                
        setExclusiveOwnerThread(null);                            //取消獨(dú)占鎖信息
    }    
    setState(c);                                                //設(shè)置鎖標(biāo)志信息                                                                                        
    return free;                                                                                            
}

看上面的源碼,表示首先先獲取state狀態(tài),如果state狀態(tài)減少1之后和0不相等則代表有重入鎖,則表示當(dāng)前線程還在占用所資源,直到線程釋放鎖返回ture標(biāo)識(shí),還是以上例子為主(此時(shí)AddDemo中的unlock不在被注釋),分析其現(xiàn)在的隊(duì)列中的狀態(tài)

釋放鎖后,進(jìn)入到if語句中,判斷當(dāng)前頭節(jié)點(diǎn)不為空且waitStatus!=0,通過上圖也可以發(fā)現(xiàn)頭節(jié)點(diǎn)為-1,則進(jìn)入到unparkSuccessor方法內(nèi):

private void unparkSuccessor(Node node) {
    /*
     * 獲取節(jié)點(diǎn)的waitStatus狀態(tài)
     */
    int ws = node.waitStatus;
      // 如果小于0則設(shè)置為0
    if (ws < 0)    
        compareAndSetWaitStatus(node, ws, 0);    

    /*
     * 喚醒下一個(gè)節(jié)點(diǎn),喚醒下一個(gè)節(jié)點(diǎn)之前需要判斷節(jié)點(diǎn)是否存在或已經(jīng)被取消了節(jié)點(diǎn),如果沒有節(jié)點(diǎn)則不需喚醒操作,如果下一個(gè)節(jié)點(diǎn)被取消了則一直一個(gè)沒有被取消的節(jié)點(diǎn)。
     */
    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);
}

可以看到它是現(xiàn)將頭節(jié)點(diǎn)的狀態(tài)更新為0,然后再喚醒下一個(gè)節(jié)點(diǎn),如果下一個(gè)節(jié)點(diǎn)為空則直接返回不喚醒任何節(jié)點(diǎn),如果下一個(gè)節(jié)點(diǎn)被取消了,那么它會(huì)從尾節(jié)點(diǎn)往前進(jìn)行遍歷,遍歷與頭節(jié)點(diǎn)最近的沒有被取消的節(jié)點(diǎn)進(jìn)行喚醒操作,在喚醒前看一下隊(duì)列狀態(tài):

然后喚醒節(jié)點(diǎn)后他會(huì)進(jìn)入到parkAndCheckInterrupt方法里面,再次去執(zhí)行下面的方法:

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;                            //取消節(jié)點(diǎn)標(biāo)志位
    try {
        boolean interrupted = false;                //中斷標(biāo)志位
        for (;;) {
            final Node p = node.predecessor();        //獲取前節(jié)點(diǎn)
            if (p == head && tryAcquire(arg)) {        //這里的邏輯是如果前節(jié)點(diǎn)為頭結(jié)點(diǎn)并且獲取到鎖則進(jìn)行頭結(jié)點(diǎn)變換
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&    //設(shè)置waitStatus狀態(tài)
                parkAndCheckInterrupt())                    //掛起線程
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);                            //取消操作
    }
}

此時(shí)獲取p==head成立,并且可以正搶到所資源,所以它會(huì)進(jìn)入到循環(huán)體內(nèi),進(jìn)行設(shè)置頭結(jié)點(diǎn)為當(dāng)前節(jié)點(diǎn),前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置為null,返回中斷標(biāo)志,看一下此時(shí)隊(duì)列情況,如下圖所示:

AbstractQueuedSynchronizer的獨(dú)占模式其實(shí)提供了三種不同的形式進(jìn)行獲取鎖操作,看一下下表所示:

方法名稱 方法描述 對(duì)應(yīng)調(diào)用的內(nèi)部方法
acquire 以獨(dú)占模式進(jìn)行不間斷的獲取鎖 tryAcquire,acquireQueued
acquireInterruptibly 以獨(dú)占模式相應(yīng)中斷的方式獲取鎖,發(fā)生中斷拋出異常 tryAcquire,doAcquireInterruptibly
tryAcquireNanos 以獨(dú)占模式相應(yīng)中斷的方式并且在指定時(shí)間內(nèi)獲取鎖,會(huì)阻塞一段時(shí)間,如果還未獲得鎖直接返回,發(fā)生中斷拋出異常 tryAcquire,doAcquireNanos

通過上面圖可以發(fā)現(xiàn),他都會(huì)調(diào)用圖表一中需要用戶實(shí)現(xiàn)的方法,ReentrantLock實(shí)現(xiàn)了獨(dú)占模式則內(nèi)部實(shí)現(xiàn)的是tryAcquiretryRelease方法,用來嘗試獲取鎖和嘗試釋放鎖的操作,其實(shí)上面內(nèi)容我們用的是ReentrantLock中的lock方法作為同步器,細(xì)心的朋友會(huì)發(fā)現(xiàn),這個(gè)lock,方法是ReentrantLock實(shí)現(xiàn)的,它內(nèi)部調(diào)用了acquire方法,實(shí)現(xiàn)了不間斷的獲取鎖機(jī)制,ReentrantLock中還有一個(gè)lockInterruptibly方法,它內(nèi)部直接調(diào)用的是AbstractQueuedSynchronizeracquireInterruptibly方法,兩個(gè)之間的區(qū)別在于,兩者都會(huì)相應(yīng)中斷信息,前者不會(huì)做任何處理還會(huì)進(jìn)入等待狀態(tài),而后者則拋出異常終止操作,

這里為了詳細(xì)看清楚它內(nèi)部關(guān)系我這里用張圖來進(jìn)行闡述,如下所示:

左側(cè)代表的事ReentrantLock,右側(cè)代表的AQS

左側(cè)內(nèi)部黃色區(qū)域代表NonfairSync

圖中1和2代表AQS調(diào)用其他方法的過程

接下來我們來看一下源碼信息:

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

發(fā)現(xiàn)他調(diào)用的Sync類中的acquireInterruptibly方法,但其實(shí)這個(gè)方法是AQS中的方法,源碼如下所示:

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())                                                //判斷線程是否被中斷
        throw new InterruptedException();                        //中斷則拋出異常
    if (!tryAcquire(arg))                                                        //嘗試獲取鎖
        doAcquireInterruptibly(arg);                                //進(jìn)行添加隊(duì)列,并且修改前置節(jié)點(diǎn)狀態(tài),且響應(yīng)中斷拋出異常
}

通過上面的源碼,它也調(diào)用了子類實(shí)現(xiàn)的tryAcquire方法,這個(gè)方法和我們上文提到的tryAcquire是一樣,ReentrantLock下的NonfairSync下的tryAcquire方法,這里這個(gè)方法就不多說了詳細(xì)請(qǐng)看上文內(nèi)容,這里主要講一下doAcquireInterruptibly這個(gè)方法:

private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);        //將節(jié)點(diǎn)添加到隊(duì)列尾部
    boolean failed = true;                                //失敗匹配機(jī)制
    try {
        for (;;) {
            final Node p = node.predecessor();            //獲取前節(jié)點(diǎn)
            if (p == head && tryAcquire(arg)) {            //如果前節(jié)點(diǎn)為頭節(jié)點(diǎn)并且獲得了鎖
                setHead(node);                            //設(shè)置當(dāng)前節(jié)點(diǎn)為頭節(jié)點(diǎn)
                p.next = null; // help GC                //頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置為null
                failed = false;                            //匹配失敗變?yōu)閒alse
                return;
            }        
            if (shouldParkAfterFailedAcquire(p, node) &&    //將前節(jié)點(diǎn)設(shè)置為-1,如果前節(jié)點(diǎn)為取消節(jié)點(diǎn)則往前一直尋找直到修改為-1為止。
                parkAndCheckInterrupt())                    //掛起線程返回是否中斷
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

其實(shí)這個(gè)方法和acquireQueued區(qū)別在于以下幾點(diǎn):

acquireQueued是在方法內(nèi)部添加節(jié)點(diǎn)到隊(duì)列尾部,而doAcquireInterruptibly是在方法內(nèi)部進(jìn)行添加節(jié)點(diǎn)到尾部,這個(gè)區(qū)別點(diǎn)并不是很重要

重點(diǎn)是acquireQueued響應(yīng)中斷,但是他不會(huì)拋出異常,而后者會(huì)拋出異常throw new InterruptedException()

分析到這里我們來用前面的例子來進(jìn)行模擬一下中中斷的操作,詳細(xì)代碼如下所示:

public class ReentrantLockDemo {

    public static void main(String[] args) throws Exception {
        AddDemo runnalbeDemo = new AddDemo();
        Thread thread = new Thread(runnalbeDemo::add);
        thread.start();
        Thread.sleep(500);
        Thread thread1 = new Thread(runnalbeDemo::add);
        thread1.start();
        Thread.sleep(500);
        Thread thread2 = new Thread(runnalbeDemo::add);
        thread2.start();
        Thread.sleep(500);
        Thread thread3 = new Thread(runnalbeDemo::add);
        thread3.start();
        Thread.sleep(10000);
        thread1.interrupt();
        System.out.println(runnalbeDemo.getCount());
    }

    private static class AddDemo {
        private final AtomicInteger count = new AtomicInteger();
        private final ReentrantLock reentrantLock = new ReentrantLock();
        private final Condition condition = reentrantLock.newCondition();

        private void add() {
            try {
                reentrantLock.lockInterruptibly();
                count.getAndIncrement();
            } catch (Exception ex) {
                System.out.println("線程被中斷了");
            } finally {
//                reentrantLock.unlock();
            }
        }

        int getCount() {
            return count.get();
        }
    }
}

上面的例子其實(shí)和前面提到的例子沒有什么太大的差別主要的差別是將lock替換為lockInterruptibly,其次就是在三個(gè)線程后面講線程1進(jìn)行中斷操作,這里入隊(duì)的操作不在多說,因?yàn)椴僮鲀?nèi)容和上面大致相同,下面是四個(gè)個(gè)線程操作完成的狀態(tài)信息:


如果線程等待的過程中拋出異常,則當(dāng)前線程進(jìn)入到finally中的時(shí)候failed為true,因?yàn)樾薷脑撟侄沃挥蝎@取到鎖的時(shí)候才會(huì)修改為false,進(jìn)來之后它會(huì)運(yùn)行cancelAcquire來進(jìn)行取消當(dāng)前節(jié)點(diǎn),下面我們先來分析下源碼內(nèi)容:

private void cancelAcquire(Node node) {
    // 如果節(jié)點(diǎn)為空直接返回,節(jié)點(diǎn)不存在直接返回
    if (node == null)
        return;
        // 設(shè)置節(jié)點(diǎn)所在的線程為空,清除線程操作
    node.thread = null;

    // 獲取當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)
    Node pred = node.prev;
      // 如果前節(jié)點(diǎn)是取消節(jié)點(diǎn)則跳過前節(jié)點(diǎn),一直尋找一個(gè)不是取消節(jié)點(diǎn)為止
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;

       // 獲取頭節(jié)點(diǎn)下一個(gè)節(jié)點(diǎn)
    Node predNext = pred.next;

    // 這里直接設(shè)置為取消節(jié)點(diǎn)狀態(tài),沒有使用CAS原因是因?yàn)橹苯釉O(shè)置只有其他線程可以跳過取消的節(jié)點(diǎn)
    node.waitStatus = Node.CANCELLED;

    // 如果當(dāng)前節(jié)點(diǎn)為尾節(jié)點(diǎn),并且設(shè)置尾節(jié)點(diǎn)為找到的合適的前節(jié)點(diǎn)時(shí),修改前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)為null
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        // 如果不是尾節(jié)點(diǎn),則說明是中間節(jié)點(diǎn),則需要通知后續(xù)節(jié)點(diǎn),嘿,伙計(jì)你被喚醒了。
        int ws;
        if (pred != head &&                                                            //前節(jié)點(diǎn)不是頭結(jié)點(diǎn)
            ((ws = pred.waitStatus) == Node.SIGNAL ||        // 前節(jié)點(diǎn)的狀態(tài)為SIGNAL 
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) //或者前節(jié)點(diǎn)狀態(tài)小于0而且修改前節(jié)點(diǎn)狀態(tài)為SIGNAL成功 
               && pred.thread != null) {                                            //前節(jié)點(diǎn)線程不為空
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next);
        } else {
              //喚醒下一個(gè)不是取消的節(jié)點(diǎn)
            unparkSuccessor(node);
        }

        node.next = node; // help GC
    }
}

首先找到當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn),如果前節(jié)點(diǎn)為取消節(jié)點(diǎn)則一直往前尋找一個(gè)節(jié)點(diǎn)。

取消的是尾節(jié)點(diǎn),則直接將前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置為null

如果取消的是頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn),且不是尾節(jié)點(diǎn)的情況時(shí),它是喚醒下一個(gè)節(jié)點(diǎn),喚醒之前并沒有將其移除隊(duì)列,而是在喚醒下一個(gè)節(jié)點(diǎn)的時(shí)候,shouldParkAfterFailedAcquire里面將取消的節(jié)點(diǎn)移除隊(duì)列,喚醒之后,當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)也設(shè)置成自己,幫助GC回收它。

如果取消節(jié)點(diǎn)是中間的節(jié)點(diǎn),則直接將其前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置為取消節(jié)點(diǎn)的下下個(gè)節(jié)點(diǎn)即可。

第一種情況如果我們?nèi)∠墓?jié)點(diǎn)是前節(jié)點(diǎn)是頭節(jié)點(diǎn),此時(shí)線程1的節(jié)點(diǎn)應(yīng)該是被中斷操作,此時(shí)進(jìn)入到cancelAcquire之后會(huì)進(jìn)入else語句中,然后進(jìn)去到unparkSuccessor方法,當(dāng)進(jìn)入到這個(gè)方法之前我們看一下狀態(tài)變化:

我們發(fā)現(xiàn)線程1的Node節(jié)點(diǎn)的waitStatus變?yōu)?也就是Node.CANCELLED節(jié)點(diǎn),然后運(yùn)行unparkSuccessor方法,該方法上面就已經(jīng)講述了其中的源碼,這里就不在貼源碼了,就是要喚醒下一個(gè)沒有被取消的節(jié)點(diǎn),這里是Ref-695這個(gè)線程,當(dāng)Ref-695被喚醒之后它會(huì)繼續(xù)運(yùn)行下面的內(nèi)容:

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)) {                //再一次循環(huán)發(fā)現(xiàn)還是沒有爭搶到鎖
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&    //再一次循環(huán)之后有運(yùn)行到這里了
                parkAndCheckInterrupt())                    //這里被喚醒了,又要進(jìn)行循環(huán)操作了
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

發(fā)現(xiàn)再一次循環(huán)操作后,還是沒有正搶到鎖,這時(shí)候還是會(huì)運(yùn)行shouldParkAfterFailedAcquire方法,這個(gè)方法內(nèi)部發(fā)現(xiàn)前節(jié)點(diǎn)的狀態(tài)是Node.CANCELLED這時(shí)候它會(huì)在內(nèi)部先將節(jié)點(diǎn)給干掉,也就是這個(gè)代碼:

if (ws > 0) {
    /*
     * Predecessor was cancelled. Skip over predecessors and
     * indicate retry.
     */
    do {
        node.prev = pred = pred.prev;
    } while (pred.waitStatus > 0);
    pred.next = node;
}

最后還是會(huì)被掛起狀態(tài),因?yàn)闆]有釋放鎖操作,最后移除的節(jié)點(diǎn)如下所示:

如果取消的事尾節(jié)點(diǎn),也就是線程3被中斷操作,這個(gè)是比較簡單的直接將尾節(jié)點(diǎn)刪除即可,其中會(huì)走如下代碼:

if (node == tail && compareAndSetTail(node, pred)) {
    compareAndSetNext(pred, predNext, null);
}

如果取消的節(jié)點(diǎn)是中間的節(jié)點(diǎn),通過上例子中則是取消線程2,其實(shí)它內(nèi)部只是將線程取消線程的前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)指向了取消節(jié)點(diǎn)的下節(jié)點(diǎn),如下圖所示:

結(jié)束語

這章節(jié)分析的主要是ReentrantLock的內(nèi)部原理,本來公平模式和非公平模式想放在一起來寫,無奈發(fā)現(xiàn)篇幅有點(diǎn)長了,所以就分開進(jìn)行寫,這樣讀取來不會(huì)那么費(fèi)勁,內(nèi)部還有條件內(nèi)容等待下章節(jié)分析,如果有分析不到位的請(qǐng)大家指正。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74758.html

相關(guān)文章

  • 圖解AQS原理ReentrantLock詳解-公平

    摘要:概述前面已經(jīng)講解了關(guān)于的非公平鎖模式,關(guān)于非公平鎖,內(nèi)部其實(shí)告訴我們誰先爭搶到鎖誰就先獲得資源,下面就來分析一下公平鎖內(nèi)部是如何實(shí)現(xiàn)公平的如果沒有看過非公平鎖的先去了解下非公平鎖,因?yàn)檫@篇文章前面不會(huì)講太多內(nèi)部結(jié)構(gòu),直接會(huì)對(duì)源碼進(jìn)行分析前文 概述 前面已經(jīng)講解了關(guān)于AQS的非公平鎖模式,關(guān)于NonfairSync非公平鎖,內(nèi)部其實(shí)告訴我們誰先爭搶到鎖誰就先獲得資源,下面就來分析一下公平...

    Taonce 評(píng)論0 收藏0
  • ReentrantLockAQS原理與源碼詳解

    摘要:所以大家看下面的圖,就是線程跑過來加鎖的一個(gè)過程。好線程現(xiàn)在就重新嘗試加鎖,這時(shí)還是用操作將從變?yōu)椋藭r(shí)就會(huì)成功,成功之后代表加鎖成功,就會(huì)將設(shè)置為。此外,還要把加鎖線程設(shè)置為線程自己,同時(shí)線程自己就從等待隊(duì)列中出隊(duì)了。 ReentrantLock和AQS的關(guān)系 ReentrantLock使用 showImg(https://segmentfault.com/img/bVbkZr4?...

    elisa.yang 評(píng)論0 收藏0
  • Java 重入 ReentrantLock 原理分析

    摘要:的主要功能和關(guān)鍵字一致,均是用于多線程的同步。而僅支持通過查詢當(dāng)前線程是否持有鎖。由于和使用的是同一把可重入鎖,所以線程可以進(jìn)入方法,并再次獲得鎖,而不會(huì)被阻塞住。公平與非公平公平與非公平指的是線程獲取鎖的方式。 1.簡介 可重入鎖ReentrantLock自 JDK 1.5 被引入,功能上與synchronized關(guān)鍵字類似。所謂的可重入是指,線程可對(duì)同一把鎖進(jìn)行重復(fù)加鎖,而不會(huì)被阻...

    lx1036 評(píng)論0 收藏0
  • Java多線程進(jìn)階(七)—— J.U.Clocks框架:AQS獨(dú)占功能剖析(2)

    摘要:開始獲取鎖終于輪到出場了,的調(diào)用過程和完全一樣,同樣拿不到鎖,然后加入到等待隊(duì)列隊(duì)尾然后,在阻塞前需要把前驅(qū)結(jié)點(diǎn)的狀態(tài)置為,以確保將來可以被喚醒至此,的執(zhí)行也暫告一段落了安心得在等待隊(duì)列中睡覺。 showImg(https://segmentfault.com/img/remote/1460000016012467); 本文首發(fā)于一世流云的專欄:https://segmentfault...

    JayChen 評(píng)論0 收藏0
  • Java多線程進(jìn)階(三)—— J.U.Clocks框架:ReentrantLock

    摘要:公平策略在多個(gè)線程爭用鎖的情況下,公平策略傾向于將訪問權(quán)授予等待時(shí)間最長的線程。使用方式的典型調(diào)用方式如下二類原理的源碼非常簡單,它通過內(nèi)部類實(shí)現(xiàn)了框架,接口的實(shí)現(xiàn)僅僅是對(duì)的的簡單封裝,參見原理多線程進(jìn)階七鎖框架獨(dú)占功能剖析 showImg(https://segmentfault.com/img/remote/1460000016012582); 本文首發(fā)于一世流云的專欄:https...

    jasperyang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<