独占写并发读访问

有时需要进程同时写入和读取相同的数据

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 的工作方式非常不同,此样本不能转置。