[圖解Java]Condition

圖解Condition

0. demo

我先給出一個demo, 這樣你們就能夠根據我給的這段代碼, 邊調試邊看源碼了. 仍是那句話: 注意"My" , 我把ReentrantLock類 更名爲了 "MyReentrantLock"類 , "Lock"類 更名爲了"MyLock"類. 你們粘貼個人代碼的時候, 把相應的"My"都去掉就行了, 不然會編譯報錯哦.java

import java.util.Scanner;
import java.util.concurrent.locks.Condition;
import java.util.function.Supplier;

public class ConditionTest {
    static final Scanner scanner = new Scanner(System.in);
    static volatile String cmd = "";
    private static MyReentrantLock lock = new MyReentrantLock(true);
    private static Condition condition = lock.newCondition();

    public static void main(String[] args) {
        for (String name : new String[]{"w1", "w2", "w3", "w4", "w5", "w6"})
            new Thread(() -> func(() -> lock, name)).start();
        new Thread(() -> signalOne(() -> lock, "s")).start();

        while (scanner.hasNext()) {
            cmd = scanner.nextLine();
        }
    }

    public static void func(Supplier<MyLock> myLockSupplier, String name) {
        blockUntilEquals(() -> cmd, name);
        myLockSupplier.get().lock();

        System.out.println(name + "阻塞等待...");
        try {
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("釋放了" + name);

        myLockSupplier.get().unlock();
    }

    public static void signalOne(Supplier<MyLock> myLockSupplier, String name) {
        while (true) {
            blockUntilEquals(() -> cmd, name);
            myLockSupplier.get().lock();
            condition.signal();
            System.out.println("通知喚醒了一個等待...");
            myLockSupplier.get().unlock();
        }
    }

    private static void blockUntilEquals(Supplier<String> cmdSupplier, final String expect) {
        while (!cmdSupplier.get().equals(expect))
            quietSleep(1000);
        clearCmd();
    }

    private static void quietSleep(int mills) {
        try {
            Thread.sleep(mills);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void clearCmd() {
        cmd = "";
    }
}

使用例子在下面. ui

首先輸入w1, 讓線程1執行await() . 而後輸入w2, 讓線程2執行await(). 而後輸入w3, 讓線程3執行await().線程

接下來輸入3次 s, 沒輸入一次s, 並按下回車, 就會signal通知一個await等待.3d

1. 開始圖解Condition

想用ReentrantLock的Condition, 那麼就首先要有個ReentrantLock鎖.調試

實例化一個鎖, ReentrantLock裏只有一個成員變量sync.blog

sync實例裏面有四個成員變量.隊列

分別表示:get

          1. state - 鎖計數器cmd

          2. exclusiveOwnerThread - 鎖的持有線程源碼

          3. head - `等待隊列`的頭結點.

          4. tail - 指向`等待隊列`的最後一個元素

 而後我們實例化了一個Condition. 

 當我們輸入w1後, 第一個線程就申請了鎖, 而且申請成功.

 

 而後就執行到了await()方法.

將線程1封裝爲Node節點, 而後waitState置爲-2.  -2的含義是Condition.

 await()方法內部的第一個步驟就是把當前線程(線程1)插入到了`條件隊列`中.

 

而後就開始釋放當前線程(線程1)的鎖了, 並且是徹底釋放, 一次就釋放掉所有重入次數哦, 也就是直接讓state等於0. 

釋放完鎖了, 而後掛起線程1.

 

而後讓線程2進行await.( 也就是前面的demo程序中在控制檯輸入了w2.)

線程2執行await()以前固然是先獲取鎖了.

因爲此時, 鎖是空閒的. 因此線程2成功獲取到了鎖. 淡橙色的陰影部分爲變化的內容:

  

獲取鎖以後, 線程2就該執行await()了:

 

 如上圖, 將線程封裝爲Node, 而後尾插到`條件隊裏`中, 只是await() 方法的第一步.

而後的操做, 就是徹底釋放線程2的鎖, 而後掛起線程.

 

 

若是這個時候我們在上面demo程序的控制檯輸入"s", 那麼就會讓線程s 申請鎖, 申請成功後, 就會執行signal.

首先是線程s申請鎖成功:

 線程s成功獲取了鎖之後, 就是該執行signal()了.

 首先將`條件隊列`裏的第一個節點脫離出來:

 而後把waitState從-2改成0 :

隨後要作的就是把從`條件隊列`中脫離出來的Node(就是線程1對應的Node節點), 尾插到`等待隊列`中.

可是`等待隊列`此時還未被初始化, 因此插入到`等待隊列`以前, 要把`等待隊列`初始化了. 見下圖:

 `等待隊列`初始化完了. 接下來就是把線程1對應的Node, 尾插到`等待隊列`中了:

 

而後將當前尾插的那個節點的前驅的waitState置爲-1.  -1表示下一個節點等待着被喚醒. 

 

接下來就是線程s會執行到unlock(). 而後就會釋放鎖, 以後就是喚醒`等待隊列`中的第一個線程.

如此就介紹完了signal()

 

一個signal命令, 就把一個await的線程從`條件隊列中`移到了`等待隊列`中. 到了等待隊列中以後, 剩下的就是跟"鎖解鎖後, 喚醒下一個執行"這樣的步驟同樣了.

我以爲await方法就是將線程尾插到`條件隊列`中. signal()方法就是把條件隊列中的第一個元素, 尾插入到`等待隊列`中.

因此我以爲沒必要往下分析了.

也多是我理所固然了, 有問題的話以後再補充.

相關文章
相關標籤/搜索