[圖解Java]讀寫鎖ReentrantReadWriteLock

來源:互聯網
上載者:User

標籤:asn   使用   count   name   gets   改名   現在   als   try   

圖解ReentrantReadWriteLock

如果之前使用過讀寫鎖, 那麼可以直接看本篇文章. 如果之前未使用過, 那麼請配合我的另一篇文章一起看: [源碼分析]讀寫鎖ReentrantReadWriteLock

 

0. demo

我先給出一個demo, 這樣大家就可以根據我給的這段代碼, 邊調試邊看源碼了. 還是那句話: 注意"My" , 我把ReentrantReadWriteLock類 改名為了 "MyReentrantReadWriteLock"類 , "Lock"類 改名為了"MyLock"類. 大家粘貼My Code的時候, 把相應的"My"都去掉就好了, 否則會編譯報錯哦.

demo裡是一個公平讀寫鎖

import java.util.HashMap;import java.util.Map;import java.util.Scanner;import java.util.concurrent.locks.Lock;import java.util.function.Supplier;public class ReentrantReadWriteLockTest2 {    static final Scanner scanner = new Scanner(System.in);    static volatile String cmd = "";    private static MyReentrantReadWriteLock lock = new MyReentrantReadWriteLock(true);    private static MyReentrantReadWriteLock.ReadLock readLock = lock.readLock();    private static MyReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();    public static void main(String[] args) {        for (Map.Entry<String, Lock> entry : new HashMap<String, Lock>() {{            for (int i = 0; i < 10; i++) {                put("r" + i, readLock);                put("w" + i, writeLock);            }        }}.entrySet()) {            new Thread(() -> func(entry::getValue, entry.getKey())).start();        }        while (scanner.hasNext()) {            cmd = scanner.nextLine();        }    }    public static void func(Supplier<Lock> myLockSupplier, String name) {        String en_type = myLockSupplier.get().getClass().getSimpleName().toLowerCase().split("lock")[0];        String zn_type = (en_type.equals("read") ? "讀" : "寫");        blockUntilEquals(() -> cmd, "lock " + en_type + " " + name);        myLockSupplier.get().lock();        System.out.println(name + "擷取了" + zn_type + "鎖");        blockUntilEquals(() -> cmd, "unlock " + en_type + " " + name);        myLockSupplier.get().unlock();        System.out.println(name + "釋放了" + zn_type + "鎖");    }    private static void blockUntilEquals(Supplier<String> cmdSupplier, final String expect) {        while (!cmdSupplier.get().equals(expect))            quietSleep(1000);    }    private static void quietSleep(int mills) {        try {            Thread.sleep(mills);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

使用例子在下面. 

我們可以看到r1持有了讀鎖之後, r2來申請讀鎖, 也可以成功. 說明讀鎖是可以共用的.

接下來寫鎖

1. 開始圖解 (公平讀寫鎖)

咱們執行個體化一個讀寫鎖後, 鎖的狀態大致如:

此時鎖是空閑狀態.

如果這個時候r1來申請讀鎖.那麼就可以直接成功, 變化如下的黑色陰影部分.

firstReader 是線程的引用. 讀鎖是共用的, 可以有很多線程來擷取讀鎖. 而firstReader是記錄這些持有讀鎖線程中第一個獲得讀鎖的線程的.

firstReaderHoldCount是 firstReader引用的線程的讀鎖獲得次數(也就是firstReader重入的次數)

 

 接下來如果r2來申請讀鎖, 會發生什麼?

r2會申請成功, 而且變化如下:

 其中cacheHoldCounter是一個引用, 總是指向最後一個獲得讀鎖的線程的計數器.

 

接下來讓w1線程申請寫鎖. 寫鎖和讀鎖是互斥的, 所以寫鎖無法申請成功, 於是會進入到`等待隊列`.

由於等待隊列是懶初始化, 所以這個時候才會產生等待隊列的頭結點:

 然後就是把w1對應的Node尾插到`等待隊列`中了:

 然後再把當前節點的前驅節點的waitStatus置為-1.  -1表示後繼節點在等待線程被啟用. 

然後線程w1就放心地掛起了:

 

接下來咱們再讓r3線程擷取讀鎖會怎麼樣呢?

(咱們現在示範的是公平鎖, 如果有線程在隊列裡等待的話, 後續申請讀鎖的線程就不會直接拿到讀鎖, 而是進入到等待隊列中. 畢竟寫鎖先來的嘛, 不能插隊.) 

線程r3進入到了`等待隊列`中.然後線程r3掛起了. 變化如的黑色陰影部分所示.

 

接下來咱們讓r4申請讀鎖, 最終結果和r3一樣, 就是進入到了`等待隊列`的最末尾. (但是這個r4在後續的講解中有用)

所以r4就不用講了, 和r3一樣:

 

 

接下來咱們釋放r1的讀鎖:

 

然後釋放r2的讀鎖:

(cachedHoldCounter我沒有加陰影, 是因為, 他其實並不是真的變為null了, 還是指向原來的那個元素, 但是這個已經不重要了.)

 

當線程r2釋放讀鎖的時候發現讀鎖已經被完全釋放了, 所以會啟用`等待隊列` 裡的第一個線程.

並且讓第一個線程對應的Node作為新的Head. 淘汰掉原先的Head.

 

釋放w1的寫鎖:

線程w1釋放了讀鎖後, 啟用了自己的後繼節點r3.

 

r3被啟用後,開始準備擷取讀鎖.

 把firstReader指向自己後, 把自己替換為新的Head節點:

 線程r3申請完讀鎖後, 查看後繼節點的nextWaiter是否等於Node.SHARED. 如果是, 那麼就會喚醒這個後繼節點.

 

所以接下來會喚醒r4: 

 現在r4被啟用了, r4開始申請讀鎖了:

 然後r4即將成為新的Head節點:

到這裡, demo裡的示範部分就完成了.

 

最後, 咱們依次把r3 和 r4 也都釋放了吧. 反正也剩的不多了.

釋放了r3的時候, 變化如下:

 

最後咱們釋放掉r4:

 (其中的cachedHoldCounter並不是真正地變為了null, 而是還在指向著原來的元素. 只是在這裡顯得沒用了, 所以那部分沒話)

 

線程r4執行完之後, 所有的線程就都釋放了. 鎖的狀態如下: 

 到這裡, 整個公平讀寫鎖的申請鎖, 釋放鎖的過程, 就都示範完了. 

[圖解Java]讀寫鎖ReentrantReadWriteLock

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.