Java1.8引入了一個新鎖StampedLock,這個鎖能夠認爲是ReadWriteLock的改進。node
咱們知道在ReadWriteLock中寫和讀是互斥的,也就是若是有一個線程在寫共享變量的話,其餘線程讀共享變量都會阻塞。bash
StampedLock把讀分爲了悲觀讀和樂觀讀,悲觀讀就等價於ReadWriteLock的讀,而樂觀讀在一個線程寫共享變量時,不會被阻塞,樂觀讀是不加鎖的。因此沒鎖確定是比有鎖的性能好,這樣的話在大併發讀狀況下效率就更高了!併發
StampedLock的用法稍稍有點不一樣,在獲取鎖和樂觀讀時,都會返回一個stamp,解鎖時須要傳入這個stamp,在樂觀讀時是用來驗證共享變量是否被其餘線程寫過。來看一下官方示例性能
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock(); //獲取寫鎖
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp); //釋放寫鎖
}
}
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //樂觀讀
double currentX = x, currentY = y;
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
// Could instead start with optimistic, not read mode
long stamp = sl.readLock(); //獲取讀鎖
try {
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp); //升級爲寫鎖
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
}
else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}
複製代碼
其上的操做在樂觀讀時,若是有寫操做修改了共享變量則升級樂觀讀爲悲觀讀鎖,這樣避免樂觀讀反覆的循環等待寫鎖的釋放,避免浪費CPU資源。因此在咱們的使用StampedLock的時候,建議這樣操做。ui
看起來好像StampedLock性能又比ReadWriteLock鎖好,那是否是均可以用StampedLock拋棄ReadWriteLock?this
並非的,StampedLock不是可重入鎖,因此不支持重入,而且StampedLock不支持條件變量,也就是沒Condition。若是是線程使用writeLock()或者readLock()得到鎖以後,線程還沒執行完就被interrupt()的話,會致使CPU飆升....坑啊 咱們來看下源碼spa
public long readLock() {
long s = state, next; // bypass acquireRead on common uncontended case
return ((whead == wtail && (s & ABITS) < RFULL &&
U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
next : acquireRead(false, 0L)); //當CAS失敗以後就會嘗試申請鎖,注意第一個參數是false
}
public long writeLock() {
long s, next; // bypass acquireWrite in fully unlocked case only
return ((((s = state) & ABITS) == 0L &&
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
next : acquireWrite(false, 0L)); //當CAS失敗以後就會嘗試申請鎖,注意第一個參數是false
}
//就拿acquireWrite舉例,acquireRead也是相似的。
private long acquireWrite(boolean interruptible, long deadline) {
WNode node = null, p;
for (int spins = -1;;) { // spin while enqueuing
//省略代碼無數
if (interruptible && Thread.interrupted())
return cancelWaiter(node, node, true);
}
}
複製代碼
首先裏面是個無限循環,而後 if (interruptible && Thread.interrupted())
已經得知調用的interruptible參數傳入的是false,因此Thread.interrupted()
也不會執行到,也必定調用不到cancelWaiter
,因此就一直循環循環,CPU使用率就會漲漲漲。線程
因此若是要使用中斷功能就得用readLockInterruptibly()
或者writeLockInterruptibly()
來得到鎖。code
若有錯誤歡迎指正! 我的公衆號:yes的練級攻略資源