獨佔寫併發讀訪問

有時需要程序同時寫入和讀取相同的資料

ReadWriteLock 介面及其 ReentrantReadWriteLock 實現允許訪問模式,可以描述如下:

  1. 可以有任意數量的併發資料讀取器。如果授予至少一個讀者訪問許可權,則無法訪問作者。
  2. 資料最多只能有一個編寫器。如果授予了寫入者訪問許可權,則沒有讀者可以訪問該資料。

實現可能如下所示:

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 :讀取部件上的鎖是非常需要的,儘管對於不經意的讀者來說可能看起來不那麼明顯。實際上,如果你沒有鎖定閱讀器方面,任何數量的東西都可能出錯,其中包括:

  1. 原始值的寫入並不保證在所有 JVM 上都是原子的,因此如果 data 是 64 位長型別,讀者可以看到例如 64 位寫入中只有 32 位
  2. 只有當我們在寫入和讀取之間建立 Happen Before 關係時,JVM 才能保證來自未執行它的執行緒的寫入的可見性。當讀者和作者都使用各自的鎖時,就建立了這種關係,但不是這樣

Version >= Java SE 8

如果需要更高的效能,在某些型別的使用下,有一種更快的鎖定型別,稱為 StampedLock,其中包括實現樂觀鎖定模式。此鎖定與 ReadWriteLock 的工作方式非常不同,此樣本不能轉置。