1. 程式人生 > 實用技巧 >【Java多執行緒】讀寫鎖ReentrantReadWriteLock

【Java多執行緒】讀寫鎖ReentrantReadWriteLock

1.讀寫鎖介紹

鎖(如Mutex和ReentrantLock)基本都是排他鎖,在同一時刻只能同一個執行緒訪問。而讀寫鎖在同時可以允許多個讀執行緒訪問,但是在寫執行緒訪問時,所有的讀執行緒和其他寫執行緒均被阻塞。

讀鎖不支援條件變數。

2.ReentrantReadWriteLock 特性

在這裡插入圖片描述
只能降級,不能升級

3.ReentrantReadWriteLock API

ReadWriteLock介面僅定義了獲取讀鎖和寫鎖的兩個方法,即readLock()方法和writeLock()方法。
而其實現——ReentrantReadWriteLock,除了介面方法之外,還提供了一些便於外界監控其內部工作狀態的方法,如圖。

在這裡插入圖片描述

4.讀寫鎖的實現分析

4.1 讀寫狀態的設計

4.1.1

讀寫鎖的自定義同步器需要在同步狀態(一個整型變數)上維護多個讀執行緒和一個寫執行緒的狀態。
讀寫鎖將變數切割成兩個部分,高16位表示讀,低16位表示寫
在這裡插入圖片描述
[上圖表明一個執行緒已經獲取了寫鎖,且重進入2次,同時也連續獲取了2次讀鎖。]

4.1.2 通過位運算快速確定讀寫狀態

假設當前同步狀態為S,
求寫狀態:S&0000FFFF,這樣相當於抹去高16位(任意數&0=0);
求讀狀態:S>>>16(右移16位,無符號前面補0)。

寫狀態增加1:S+1;
讀狀態增加1:S+(1<<16),也就是S+00010000。

根據狀態的劃分能得出一個推論:S不等於0時,當寫狀態(S&0x0000FFFF)等於0時,則讀狀態(S>>>16)大於0,即讀鎖已被獲取。

4.2 寫鎖的獲取與釋放

如果讀鎖已被獲取(讀狀態不為0)或者該執行緒不是已獲得寫鎖的執行緒,當前執行緒進入等待狀態;
如果當前執行緒已獲得寫鎖,則增加寫狀態。

[ReentrantReadWriteLock的tryAcquire方法 程式碼]
在這裡插入圖片描述
在這裡插入圖片描述

4.3 讀鎖的獲取與釋放

在沒有寫執行緒訪問(或寫狀態為0時),讀鎖總被成功獲取,所做的只是(執行緒安全地,依靠CAS保證)增加讀狀態;
如果已有寫鎖被其他執行緒獲取,進入等待狀態。

[ReentrantReadWriteLock的tryAcquireShared方法 程式碼]
在這裡插入圖片描述

讀鎖的每次釋放減少讀狀態(1<<16)

4.4 鎖降級

鎖降級:寫鎖降級為讀鎖。把持住當前擁有的寫鎖,再獲取到讀鎖,隨後釋放先前擁有的寫鎖。

5.讀寫鎖原理

讀寫鎖使用的是同一個Sync同步器,因此等待佇列、state等也是同一個。

5.1 加寫鎖

在這裡插入圖片描述

5.2 加讀鎖、寫鎖失敗

在這裡插入圖片描述

5.3 寫鎖釋放,喚醒連續讀鎖(直到遇見寫鎖)

這裡喚醒t2,t3。t4要寫鎖,所以不喚醒。
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

5.4 讀鎖釋放

釋放讀鎖t2,t3,喚醒寫鎖t4。
比如釋放t2,計數-1,
在這裡插入圖片描述
在這裡插入圖片描述


在這裡插入圖片描述