ReentrantReadWriteLock 读写锁的使用及原理
一. 介绍
在之前讲到的 synchronized 和 ReentrantLock 都是排他锁,也就是同一时刻只能有一个线程获取到锁。而读写锁在同一时刻允许多个线程获取锁,读写锁顾名思义,有读锁和写锁,在读锁的时候,允许多个线程同时获取,但是在一个线程获取写锁的时候,其他获取到读锁或者写锁的线程都会阻塞。
二. 读写锁实现缓存
读写锁最常用的例子就是用锁的性质来实现一个缓存,缓存的特点就是读多写少,如果使用普通的锁来实现,每次都只能有一个线程获取锁,其他的线程都会阻塞。而读写锁就能给多个进行读操作的线程进行锁的并发。
在缓存的例子中,使用了一个非线程安全的 HashMap,但是在实际获取值的时候使用了读锁和写锁保证线程安全。
public class Cache {
private static final Map<String, Object> map;
private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static final Lock readLock = readWriteLock.readLock();
private static final Lock writeLock = readWriteLock.writeLock();
static {
map = new HashMap<>();
}
public static Object get(String key) {
readLock.lock();
try {
return map.get(key);
} finally {
readLock.unlock();
}
}
public static Object put(String key, Object value) {
writeLock.lock();
try {
return map.put(key, value);
} finally {
writeLock.unlock();
}
}
}
三. 读锁和写锁的实现
读写锁的实现也是依靠对队列同步器的自定义实现。同时还和 ReentrantLock 一样分为公平锁和非公平锁,在尝试获取锁的时候,也会根据是否是公平锁来判断是否要在等待队列中等待。
1. 读锁
读锁是一个支持多个线程获取的锁,但是读锁是和写锁排斥的,读锁实现了队列同步器中的共享式获取同步状态的方法。因为有可能会有多个线程获取锁,所以在获取锁方法的实现上,使用了 CAS 以及循环多次尝试来成功获取锁。
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.</