獨佔寫併發讀訪問
有時需要程序同時寫入和讀取相同的資料。
ReadWriteLock
介面及其 ReentrantReadWriteLock
實現允許訪問模式,可以描述如下:
- 可以有任意數量的併發資料讀取器。如果授予至少一個讀者訪問許可權,則無法訪問作者。
- 資料最多只能有一個編寫器。如果授予了寫入者訪問許可權,則沒有讀者可以訪問該資料。
實現可能如下所示:
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Sample {
// Our lock. The constructor allows a "fairness" setting, which guarantees the chronology of lock attributions.
protected static final ReadWriteLock RW_LOCK = new ReentrantReadWriteLock();
// This is a typical data that needs to be protected for concurrent access
protected static int data = 0;
/** This will write to the data, in an exclusive access */
public static void writeToData() {
RW_LOCK.writeLock().lock();
try {
data++;
} finally {
RW_LOCK.writeLock().unlock();
}
}
public static int readData() {
RW_LOCK.readLock().lock();
try {
return data;
} finally {
RW_LOCK.readLock().unlock();
}
}
}
注 1 :這個精確的用例使用 AtomicInteger
有一個更清晰的解決方案,但這裡描述的是一種訪問模式,無論這裡的資料是作為 Atomic 變體的整數,這都是有效的。
注 2 :讀取部件上的鎖是非常需要的,儘管對於不經意的讀者來說可能看起來不那麼明顯。實際上,如果你沒有鎖定閱讀器方面,任何數量的東西都可能出錯,其中包括:
- 原始值的寫入並不保證在所有 JVM 上都是原子的,因此如果
data
是 64 位長型別,讀者可以看到例如 64 位寫入中只有 32 位 - 只有當我們在寫入和讀取之間建立 Happen Before 關係時,JVM 才能保證來自未執行它的執行緒的寫入的可見性。當讀者和作者都使用各自的鎖時,就建立了這種關係,但不是這樣
Version >= Java SE 8
如果需要更高的效能,在某些型別的使用下,有一種更快的鎖定型別,稱為 StampedLock
,其中包括實現樂觀鎖定模式。此鎖定與 ReadWriteLock
的工作方式非常不同,此樣本不能轉置。