一、 ReentrantReadWriteLock简介
ReentrantReadWriteLock是在AQS的基础上实现的一个可重入锁。它的内部维护了一把读锁和一把写锁,读锁是共享锁,写锁是排他锁。这样就保证了写数据时的线程安全性,又保证了读数据时的多线程并发,比较适合读取数据较多而写数据较少的并发场景。
写一个例子演示ReentrantReadWriteLock的使用。
class RWData<T>{
private T data ;
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(true);
private ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
public RWData(){
}
public void put(T t){
try{
System.out.println("Before write lock");
writeLock.lock();
data = t;
}catch (Exception e){
e.printStackTrace();
}finally {
writeLock.unlock();
System.out.println("After write unlock");
}
}
public T get() {
try{
readLock.lock();
return data;
}catch (Exception e){
e.printStackTrace();
}finally {
readLock.unlock();
}
return null;
}
}
public class TestRWLock {
public static void main(String[] args) {
RWData<Integer> queue = new RWData<>();
for (int i = 0; i < 10; i++){
new Thread(()->{
while(true){
Integer data = queue.get();
if (null != data)
System.out.println(Thread.currentThread().getName() + "gets data " + data.intValue());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
new Thread(()->{
while(true){
int data = new Random().nextInt(1000);
queue.put(data);
System.out.println(Thread.currentThread().getName() + "puts data " + data);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
运行结果
写的时候其他的线程都需要等待,当读的时候所有的线程可以同时读。
写锁是独占的,写时不能有其他线程写也不能读;所有的独锁都释放完之前也不能加写锁。这将在代码中有体现。
二、ReentrantReadWriteLock源码解析
一个读写锁,需要维护3个数据:
- 写锁的重入次数
- 读锁的个数(读锁有多个)
- 读锁的重入次数
Sync继承AQS的state,高16位存读的,低16位存写的,Sync类的readHolds存储了每一个读锁的重入次数。
通过getState与setState来获取和保存。
ReentrantReadWriteLock内部有一个ReadLock 类和一个WriteLock类,各包含了一个Sync子类实例负责具体干活。Sync类extends AQS实现了大多数功能,但它是abstract类,需要实例化子类FairSync和NonfairSync来干活。它们分别实现了公平方式和非公平方式。
创建ReentrantReadWriteLock对象时要选择公平还是非公平,公平就用FairSync,非公平就用NonfairSync,默认非公平。
-
ReadLock
ReadLock的lock调用了内部的sync实例的acquireShared()
acquireShared调用了ReadLock中存的sync实例的tryAcquireShared()
unlock则调用了 --> sync.releaseShared() --> Sync子类实例的 tryReleaseShared。
-
WriteLock
writeLock的lock调用了--> sync.acquire() --> Sync子类实例的tryAcquire
unlock调用了--> sync.acquire() --> Sync子类实例的tryAcquire
这些代码的意义与上一节 (7)AbstractQueuedSynchronizer和ReentrantLock—— 可重入锁的实现 基本一致,无需赘述。
writerShouldBlock看是否需要使其他的线程阻塞。