前一陣有個作反抄襲檢測的小夥伴問了我一個問題。javascript
---
在多線程裏就是有個變量,我須要讀取它來判斷是否給它寫入一些信息。
打算加鎖,可是若是讀取時候加入readlock,寫入時候加入writelock,
這樣作可能讀寫不一樣步。可是若是一塊兒加lock效果就跟synchronized同樣,效率變差
---複製代碼
其實他的問題就是下面的場景:html
我去研究了下讀寫鎖,後來發現其實java官方已經給了答案。使用讀寫鎖加標誌位解決讀寫不一樣步的問題。Java ReentrantReadWriteLock
在這先普及下讀寫鎖。java
##代碼實例api
直接拿Java官方示例解讀了。多線程
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock(); //1. 上讀鎖
if (!cacheValid) { //2. 驗證cacheValid
// Must release read lock before acquiring write lock
rwl.readLock().unlock(); //3. 解除讀鎖
rwl.writeLock().lock(); //4. 上寫鎖
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) { //5. 驗證cacheValid
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock(); //6. 上讀鎖
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read //7. 解除寫鎖
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();//8. 解除讀鎖
}
}
}複製代碼
好比如今有線程ABCDE五個線程,使用processCachedData()方法,接下來會發現以下步驟。併發
-> DE線程滯後,ABC同時進入到步驟1. 上讀鎖
-> ABC進入到步驟2. 驗證cacheValid。(新實例中cacheValid初始爲fasle,因此進入if條件句中)
-> ABC執行步驟3. 解除讀鎖。
-> 假設此時A率先完成步驟4. 上寫鎖。
-> BC沒法獲取寫鎖,處於等待狀態,被阻塞在步驟4。 上寫鎖。此時D線程執行使用processCachedData()方法,被阻塞在步驟1. 上讀鎖。
-> A進入到步驟5. 驗證cacheValid。
步驟五很關鍵,若是線程A寫完後,解除了寫鎖,此時新的線程E獲取到了寫鎖,就會寫入新數據,此時就不是同步鎖了,程序出錯。
-> A修改數據,cacheValid置爲true。步驟6和步驟7是寫鎖的降級操做,即寫鎖釋放的時候,先降級爲讀鎖,這樣其餘等待獲取寫鎖的線程會繼續等待,而後再釋放寫鎖,保證同步性。
-> A執行完步驟8,BC阻塞結束,其中一位獲取寫鎖。oracle