现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁(读多写少)。
在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源(读读共享);但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写操作了(读写,写写互斥)。
思考:针对这种场景,有没有比ReentrantLock更好的方案?
1. 读写锁介绍
读写锁ReadWriteLock,顾名思义一把锁分为读与写两部分,读锁允许多个线程同时获得,因为读操
作本身是线程安全的。而写锁是互斥锁,不允许多个线程同时获得写锁。并且读与写操作也是互斥
的。读写锁适合多读少写的业务场景。
2. ReentrantReadWriteLock介绍
针对这种场景,JAVA的并发包提供了读写锁ReentrantReadWriteLock,它内部,维护了一对相关的锁,一个用于只读操作,称为读锁;一个用于写入操作,称为写锁,描述如下:
线程进入读锁的前提条件:
1.没有其他线程的写锁
2.没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。
线程进入写锁的前提条件:
1.没有其他线程的读锁
2.没有其他线程的写锁
而读写锁有以下三个重要的特性:
公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
可重入:读锁和写锁都支持线程重入。以读写线程为例:读线程获取读锁后,能够再次获取读锁。写线程在获取写锁之后能够再次获取写锁,同时也可以获取读锁。
锁降级:遵循获取写锁、再获取读锁最后释放写锁的次序,写锁能够降级成为读锁。
2.1 ReentrantReadWriteLock的使用
读写锁接口ReadWriteLock
一对方法,分别获得读锁和写锁 Lock 对象。
ReentrantReadWriteLock类结构
ReentrantReadWriteLock是可重入的读写锁实现类。在它内部,维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 Writer 线程,读锁可以由多个 Reader 线程同时持有。也就是说,写锁是独占的,读锁是共享的。
如何使用读写锁
public class ReadWriteLockExample {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();//读锁
private final Lock writeLock = lock.writeLock();//写锁
private final String[] data = new String[10];
public void write(int index, String value) {
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName()+"获取写锁");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// read(2);
data[index] = value;
} finally {
System.out.println(Thread.currentThread().getName()+"释放写锁");
writeLock.unlock();
}
}
public String read(int index) {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName()+"获取读锁");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return data[index];
} finally {
System.out.println(Thread.