ReadWriteLock
ReadWriteLock持有一对锁,一个是用于读操作的readLock,一个用于写操作的writeLock。readLock是共享锁,可以被多个读线程同时持有,writeLock是独占锁。
ReadWriteLock必须保证writeLock操作的内存一致性。也就是说,持有readLock的线程能看到先前writeLock释放后的所有更新。
与只使用一个独占锁相比,ReadWriteLock能对访问共享数据支持更高的并发。ReadWriteLock实现的并发场景如下:修改数据时只有一个写线程在修改,读取数据时可以有多个读线程并发读取。
ReadWriteLock是否能比独占锁拥有更高的性能,取决于数据读取的频率是否比数据修改的频率更高、读操作和写操作的持续时间、竞争读/写数据的激烈程度 —— 即在同一时间尝试读/写数据的线程数量。
ReentrantReadWriteLock
ReentrantReadWriteLock是ReadWriteLock接口的实现类,支持与ReentrantLock相似的语义。它支持以下属性:
1、非公平模式
当设置为非公平模式时,获得readLock和writeLock的顺序是不确定的。非公平锁可能会导致在竞争一直发生时,某个读线程或者某个写线程会无限期等待,但是他通常比公平锁拥有更高的吞吐量。
2、公平模式
当设置为公平模式时,对竞争锁的线程使用的是先到先得的策略。当锁释放时,等待时间最长的写线程会获得writeLock,或者如果一批读线程的等待时间比所有写线程的等待时间更长,则该读线程组会获得readLock。
尝试获得fair write lock的线程会一直阻塞,直到readLock和writeLock都已经释放(即当前无等待线程)。注意调用非阻塞方法ReadLock#tryLock()方法、writeLock#tryLock()方法不遵守这一fair约定。它们都会尝试立即获得锁,不管当前有没有等待线程。
3、可重入性
ReadLock和WriteLock都是可重入的,此外,写线程也可以获得readLock,但读线程不可以获得writeLock。这是有用的,比如在获得writeLock期间调用需要获得readLock的方法时。如果一个读线程尝试获得writeLock,实时永远不可能成功的。
4、锁降级
可重入性允许一个writeLock降级为readLock,降级方法是:获得writeLock,然后是readLock,然后释放readLock。但是将一个readLock升级为writeLock是不可能的。
5、获得锁期间是否支持中断
readLock和writeLock在获得锁期间都支持中断。
6、Condition支持
writeLock支持Condition,可以通过ReentrantLock#newCondition()方法创建Condition。但是readLock不支持Condition,调用readLock().newCondition()方法会抛出操作不支持异常。
使用示例
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}
ReentrantReadWriteLock定义了2个内部类:ReadLock和WriteLock,将他们的实例作为核心的成员变量。
/** Inner class providing readlock */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;
lock()方法
ReadLock在调用lock()方法获取读锁时,会首先判断当前是否有其他线程占有了写锁。
lockInterruptibly()方法
ReadLock在调用lockInterruptibly()方法获取读锁时,会首先判断当前是否有其他线程占有了写锁。
tryLock()方法
public static class ReadLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -5992448646407690164L;
private final Sync sync;
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() {
sync.acquireShared(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean tryLock() {
return sync.tryReadLock();
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.releaseShared(1);
}
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}