一、 ReentrantReadWriteLock簡介
ReentrantReadWriteLock是在AQS的基礎上實現的一個可重入鎖。它的內部維護了一把讀鎖和一把寫鎖,讀鎖是共享鎖,寫鎖是排他鎖。這樣就保證了寫數據時的線程安全性,又保證了讀數據時的多線程併發,比較適合讀取數據較多而寫數據較少的併發場景。
寫一個例子演示ReentrantReadWriteLock的使用。
class RWData<T>{
private T data ;
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(true);
private ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
public RWData(){
}
public void put(T t){
try{
System.out.println("Before write lock");
writeLock.lock();
data = t;
}catch (Exception e){
e.printStackTrace();
}finally {
writeLock.unlock();
System.out.println("After write unlock");
}
}
public T get() {
try{
readLock.lock();
return data;
}catch (Exception e){
e.printStackTrace();
}finally {
readLock.unlock();
}
return null;
}
}
public class TestRWLock {
public static void main(String[] args) {
RWData<Integer> queue = new RWData<>();
for (int i = 0; i < 10; i++){
new Thread(()->{
while(true){
Integer data = queue.get();
if (null != data)
System.out.println(Thread.currentThread().getName() + "gets data " + data.intValue());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
new Thread(()->{
while(true){
int data = new Random().nextInt(1000);
queue.put(data);
System.out.println(Thread.currentThread().getName() + "puts data " + data);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
運行結果
寫的時候其他的線程都需要等待,當讀的時候所有的線程可以同時讀。
寫鎖是獨佔的,寫時不能有其他線程寫也不能讀;所有的獨鎖都釋放完之前也不能加寫鎖。這將在代碼中有體現。
二、ReentrantReadWriteLock源碼解析
一個讀寫鎖,需要維護3個數據:
- 寫鎖的重入次數
- 讀鎖的個數(讀鎖有多個)
- 讀鎖的重入次數
Sync繼承AQS的state,高16位存讀的,低16位存寫的,Sync類的readHolds存儲了每一個讀鎖的重入次數。
通過getState與setState來獲取和保存。
ReentrantReadWriteLock內部有一個ReadLock 類和一個WriteLock類,各包含了一個Sync子類實例負責具體幹活。Sync類extends AQS實現了大多數功能,但它是abstract類,需要實例化子類FairSync和NonfairSync來幹活。它們分別實現了公平方式和非公平方式。
創建ReentrantReadWriteLock對象時要選擇公平還是非公平,公平就用FairSync,非公平就用NonfairSync,默認非公平。
-
ReadLock
ReadLock的lock調用了內部的sync實例的acquireShared()
acquireShared調用了ReadLock中存的sync實例的tryAcquireShared()
unlock則調用了 --> sync.releaseShared() --> Sync子類實例的 tryReleaseShared。
-
WriteLock
writeLock的lock調用了--> sync.acquire() --> Sync子類實例的tryAcquire
unlock調用了--> sync.acquire() --> Sync子類實例的tryAcquire
這些代碼的意義與上一節 (7)AbstractQueuedSynchronizer和ReentrantLock—— 可重入鎖的實現 基本一致,無需贅述。
writerShouldBlock看是否需要使其他的線程阻塞。