j.u.c.locks.AbstractQueuedSynchronizer.Node

AQS是JUC當中最核心的部分,大部分多線程講解,都不會詳細講AQS,AQS的源代碼,要看明白仍是有點困難的。可是一旦看明白了,結構仍是蠻清晰的。這裏咱們把AQS拆開,分紅幾部分來說,就會很清楚了。首先先從內部類Nde講起php

上源代碼:java

static final class Node {
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;

        volatile int waitStatus;

        volatile Node prev;

        volatile Node next;

        volatile Thread thread;

        Node nextWaiter;

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

這個NODE類是java.util.concurrent.locks.AbstractQueuedSynchronizer的內部類。主要實現了一個鏈表的數據結構中的節點。先不談這個鏈表是作什麼的,咱們先看看這個節點是怎麼構造的。數據結構

首先,這個節點有四個狀態 ,其中三在Node類中已經明肯定義,另一個狀態爲初始狀態,值爲0多線程

        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;

1 :當前節點被取消或者中斷
併發

-1:下一個節點須要被釋放this

-2:當前節點正處在等待隊列中spa

0 :不屬於任何一種.net


其次,這個節點有兩種模式,其中SHARED是共享模式,EXCLUSIVE是獨佔模式。
線程

        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;

等一下再介紹這兩個模式。先把數據結構看完。指針


而後是成員變量:

        volatile int waitStatus;

        volatile Node prev;

        volatile Node next;

        volatile Thread thread;

        Node nextWaiter;

volatile int waitStatus:當前節點的狀態,一共四種

volatile Node prev:鏈表的前一個節點

volatile Node next:鏈表的後一個節點

volatile Thread thread:這個節點所處的線程

Node nextWaiter:這個節點等待的模式(共享模式和獨佔模式)


接下去的兩個方法和三個構造方法很是簡單,就不講了。


問題1:這個鏈表是如何工做的?

用一張圖表示隊列的工做方式:

    1. 每一個節點都擁有一個指針,知道本身的前一個節點

    2. 每一個節點都有一個狀態位,表示是否佔用資源

    3. 每一個節點都要完成自旋判斷上一個節點的狀態(圖中while循環),才能真正的執行本身的邏輯

    4. 當邏輯執行完,會修改本身的節點的狀態,放開下個節點的自旋

    5. 新插入的節點,永遠在隊尾


上圖已經很是清楚的描述了這個隊列的工做方式。這就是CLH鎖的原理。其實實現鎖還有幾種不一樣的方式。(點擊瞭解更多鎖的實現原理和比較


因爲一個線程的狀態不止兩種,因此Node的狀態實現,並無用True/False表示。而是用了waitStatus表示。另外爲了操做的方便,Node並無使用單向鏈表,而是雙向鏈表。這樣操做起來會方便不少。

以下圖:

  1. 每一個節點都擁有一個指針,知道本身的前一個節點和後一個節點

  2. 每一個節點都有一個狀態位,表示是否佔用資源

  3. 每一個節點都要完成自旋判斷上一個節點的狀態(圖中while循環),才能真正的執行本身的邏輯

  4. 當邏輯執行完,會修改本身的節點的狀態,放開下個節點的自旋

  5. 新插入的節點,永遠在隊尾。隊尾節點標記新插入節點

  6. 被取消或者中斷的線程節點,會從鏈表中斷開,被刪除

還記得在講LockSupport(點擊查看源碼)中是如何掛起和恢復一個線程的嗎?Thread對象就是作這個的。

到此爲止,咱們的圖與Node類的結構是否是已經很像了呢?


問題2:共享模式和獨佔模式

共享模式和獨佔模式是兩種不一樣的鎖定資源的方式。

最經典的舉例就是對共享文件的操做。當第一個,第二個用戶都請求讀取文件內容時,並不會阻塞用戶的讀取行爲,而且能夠併發的讀取。

可是當第三個用戶請求修改的時候,就會獲取一個獨佔鎖。一旦獲取了獨佔鎖,全部共享鎖的獲取或者獨佔鎖的獲取都會被阻塞,一直等

到獨佔鎖被釋放纔會解除阻塞。

實際在JUC中讀寫鎖也是一個很典型的共享與獨佔的例子。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息