StampedLock
StampedLock是Java8引入的一種新的鎖機制,簡單的理解,可以認爲它是讀寫 鎖的一個改進版本,讀寫鎖雖然分離了讀和寫的功能,使得讀與讀之間可以完全並 發,但是讀和寫之間依然是衝突的,讀鎖會完全阻塞寫鎖,它使用的依然是悲觀的 鎖策略.如果有大量的讀線程,他也有可能引起寫線程的飢餓。
而 StampedLock 則提供了一種樂觀的讀策略,這種樂觀策略的鎖非常類似於 無鎖的操作,使得樂觀鎖完全不會阻塞寫線程。
它的思想是讀寫鎖中讀不僅不阻塞讀,同時也不應該阻塞寫。
讀不阻塞寫的實現思路
在讀的時候如果發生了寫,則應當重讀而不是在讀的時候直接阻塞寫!即讀 寫之間不會阻塞對方,但是寫和寫之間還是阻塞的!
StampedLock 的內部實現是基於 CLH 的。
廢話不多說直接上代碼:
/**
* 類說明:JDK1.8源碼自帶的示例
*/
public class StampedLockDemo {
//一個點的x,y座標
private double x,y;
/**Stamped類似一個時間戳的作用,每次寫的時候對其+1來改變被操作對象的Stamped值
* 這樣其它線程讀的時候發現目標對象的Stamped改變,則執行重讀*/
private final StampedLock sl = new StampedLock();
//【寫鎖(排它鎖)】
void move(double deltaX,double deltaY) {// an exclusively locked method
/**stampedLock調用writeLock和unlockWrite時候都會導致stampedLock的stamp值的變化
* 即每次+1,直到加到最大值,然後從0重新開始*/
long stamp =sl.writeLock(); //寫鎖
try {
x +=deltaX;
y +=deltaY;
} finally {
sl.unlockWrite(stamp);//釋放寫鎖
}
}
//【樂觀讀鎖】
double distanceFromOrigin() { // A read-only method
/**
* tryOptimisticRead是一個樂觀的讀,使用這種鎖的讀不阻塞寫
* 每次讀的時候得到一個當前的stamp值(類似時間戳的作用)
*/
long stamp = sl.tryOptimisticRead();
//這裏就是讀操作,讀取x和y,因爲讀取x時,y可能被寫了新的值,所以下面需要判斷
double currentX = x, currentY = y;
/**如果讀取的時候發生了寫,則stampedLock的stamp屬性值會變化,此時需要重讀,
* validate():比較當前stamp和獲取樂觀鎖得到的stamp比較,不一致則失敗。
* 再重讀的時候需要加讀鎖(並且重讀時使用的應當是悲觀的讀鎖,即阻塞寫的讀鎖)
* 當然重讀的時候還可以使用tryOptimisticRead,此時需要結合循環了,即類似CAS方式
* 讀鎖又重新返回一個stampe值*/
if (!sl.validate(stamp)) {//如果驗證失敗(讀之前已發生寫)
stamp = sl.readLock(); //悲觀讀鎖
try {
currentX = x;
currentY = y;
}finally{
sl.unlockRead(stamp);//釋放讀鎖
}
}
//讀鎖驗證成功後執行計算,即讀的時候沒有發生寫
return Math.sqrt(currentX *currentX + currentY *currentY);
}
//讀鎖升級爲寫鎖
void moveIfAtOrigin(double newX, double newY) { // upgrade
// 讀鎖(這裏可用樂觀鎖替代)
long stamp = sl.readLock();
try {
//循環,檢查當前狀態是否符合
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
//如果寫鎖成功
if (ws != 0L) {
stamp = ws;// 替換stamp爲寫鎖戳
x = newX;//修改數據
y = newY;
break;
}
//轉換爲寫鎖失敗
else {
//釋放讀鎖
sl.unlockRead(stamp);
//獲取寫鎖(必要情況下阻塞一直到獲取寫鎖成功)
stamp = sl.writeLock();
}
}
} finally {
//釋放鎖(可能是讀/寫鎖)
sl.unlock(stamp);
}
}
}