什么鎖比讀寫鎖性能更高?
在并發(fā)編程中,讀寫鎖 ReentrantReadWriteLock 的性能已經(jīng)算是比較高的了,因?yàn)樗鼘⒈^鎖的粒度分的更細(xì),在它里面有讀鎖和寫鎖,當(dāng)所有操作為讀操作時(shí),并發(fā)線程是可以共享讀鎖同時(shí)運(yùn)行的,這樣就無需排隊(duì)執(zhí)行了,所以執(zhí)行效率也就更高。
那么問題來了,有沒有比讀寫鎖 ReentrantReadWriteLock 性能更高的鎖呢?
答案是有的,在 Java 中,比 ReentrantReadWriteLock 性能更高的鎖有以下兩種:
- 樂觀鎖:樂觀鎖是一種非阻塞鎖機(jī)制,它是通過 Compare-And-Swap(CAS)對比并替換來進(jìn)行數(shù)據(jù)的更改的,它假設(shè)多個(gè)線程(或進(jìn)程)之間很少會發(fā)生沖突,因此不會加鎖,只有在需要修改之后,通過對比并替換來修改共享變量的值,因此它在非高并發(fā)的環(huán)境下的性能是非常高的。
- StampedLock:它是 JDK 8 中新增的鎖類型,它提供了三種鎖模式:讀鎖、寫鎖和樂觀讀鎖。相較于 ReentrantReadWriteLock,StampedLock 提供了更細(xì)粒度的控制,支持樂觀讀取操作,可以提高并發(fā)性能。
1.樂觀鎖
樂觀鎖在 Java 中最常見的實(shí)現(xiàn)就是 atomic 家族下的類,例如 AtomicInteger、AtomicLong 等,它的核心方法中使用了 CAS 對比并替換進(jìn)行變量的修改操作,如下源碼所示:
public final int incrementAndGet() {
return U.getAndAddInt(this, VALUE, 1) + 1;
}
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset, v, v + delta)); // CAS 方法
return v;
}
然而,如果是高并發(fā)環(huán)境下,那么樂觀鎖可以需要通過多次自旋才能成功修改變量的數(shù)據(jù),這種場景下,樂觀鎖的性能可能就不如 ReentrantReadWriteLock 了。
2.StampedLock
StampedLock 有三種讀寫方法:
- readLock:讀鎖,用于多線程并發(fā)讀取共享資源。
- writeLock:寫鎖,用于獨(dú)占寫入共享資源。
- tryOptimisticRead:讀樂觀鎖,用于在不阻塞其他線程的情況下嘗試讀取共享資源。
其中 readLock() 和 writeLock() 方法與 ReentrantReadWriteLock 的用法類似,而 tryOptimisticRead() 方法則是 StampedLock 引入的新方法,它用于非常短的讀操作。
因此,我們在加鎖時(shí),可以使用性能更高的讀樂觀鎖來替代傳統(tǒng)的讀鎖,如果能加鎖成功,則它可以和其他線程(即使是寫操作)一起執(zhí)行,也無需排隊(duì)運(yùn)行(傳統(tǒng)讀鎖遇到寫鎖時(shí)需要排隊(duì)執(zhí)行),這樣的話 StampedLock 的執(zhí)行效率就會更高,它是使用如下:
// 創(chuàng)建 StampedLock 實(shí)例
StampedLock lock = new StampedLock();
// 獲取樂觀讀鎖
long stamp = lock.tryOptimisticRead();
// 讀取共享變量
if (!lock.validate(stamp)) { // 檢查樂觀讀鎖是否有效
stamp = lock.readLock(); // 如果樂觀讀鎖無效,則獲取悲觀讀鎖
try {
// 重新讀取共享變量
} finally {
lock.unlockRead(stamp); // 釋放悲觀讀鎖
}
}
// 獲取悲觀讀鎖
long stamp = lock.readLock();
try {
// 讀取共享變量
} finally {
lock.unlockRead(stamp); // 釋放悲觀讀鎖
}
// 獲取寫鎖
long stamp = lock.writeLock();
try {
// 寫入共享變量
} finally {
lock.unlockWrite(stamp); // 釋放寫鎖
}
使用樂觀讀鎖的特性可以提高讀操作的并發(fā)性能,適用于讀多寫少的場景。如果樂觀讀鎖獲取后,在讀取共享變量前發(fā)生了寫入操作,則 validate 方法會返回 false,此時(shí)需要轉(zhuǎn)換為悲觀讀鎖或?qū)戞i重新訪問共享變量。