一 简介
ReentrantReadWriteLock翻译过是读写锁的意思。并发编程中我们可能会遇到读多写少的情况,面对这种情况的时候我们可以使用ReentrantLock或者synchronized来保证数据的正确性。如果上述两种方法的话,这样的话不管是读操作还是写操作都要去获取锁和是释放锁,但是实际上我们可能只需要在写的时候保证线程线程是安全的,并且写完之后读线程得到的是修改后的数据,也就是说,在写的时候是独占的,不允许其他线程写也不允许其他线程读,而当线程读的时候,因为不会做修改的操作这时数据是可以共享的线程可以并行的访问。ReentrantReadWriteLock就可以很好的解决这种读多写少的问题。下面是两种的实现方式的差异
二 使用
ReentrantReadWriteLock内部维护了两把锁,其中writeLock是独占锁也而readLock是共享锁。当使用构造方法初始化的ReentrantReadWriteLock时候内部就会初始化这两把锁,其中在使用构造方法的时候可以传一个布尔值,为true返回的两把公平锁,false为非公平锁,当不传的时候默认为fasle。下面是简单的使用代码
/**
* 初始化读写锁
*/
private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 调用readLock()获取readLock
*/
private static final Lock readLock = readWriteLock.readLock();
/**
* 调用writeLock()获取writeLock
*/
private static final Lock writeLock = readWriteLock.writeLock();
public static void main(String[] args) {
//读的线程
Runnable readThread = () -> {
readData();
};
//写的线程
Runnable writeThread = () -> {
writeData();
};
for (int i = 0; i < 10; i++) {
if (i%2 == 0){
//偶数获取写锁
Thread thread = new Thread(writeThread, "线程" + i);
thread.start();
}
//其他线程都去获取读锁
Thread thread = new Thread(readThread, "线程" + i);
thread.start();
}
}
public static void writeData() {
writeLock.lock();//其他线程不能读也不能写
try {
//处理业务逻辑
System.out.println(Thread.currentThread() + "获取到写锁");
//写锁的时间设置长的的话可以观察它的独占性
Thread.sleep(10000);
} catch (Exception e){}finally {
//没有正确的释放锁可能会导致死锁
System.out.println(Thread.currentThread() + "释放写锁");
writeLock.unlock();
}
}
public static void readData() {
readLock.lock();//其他线程不可写
try {
System.out.println(Thread.currentThread() + "获取到读锁");
//读取数据
Thread.sleep(100);
}catch (Exception e){}finally {
System.out.println(Thread.currentThread() + "释放读锁");
readLock.unlock();
}
}
上面使用读写锁的时候是比较清晰的模式都是一种锁获取锁释放,然后另一个锁获取在释放,对于这种模式的话只要不要忘记去释放锁,一般都不会产生死锁,但在使用中还可能出现下面的模式。
/**
* 模式一 先获取写在获取读
*/
public static void writeRead() {
writeLock.lock();
System.out.println("获取到写锁");
readLock.lock();
System.out.println("获取到读锁");
readLock.unlock();
System.out.println("释放读锁");
writeLock.unlock();
System.out.println("释放写锁");
}
/**
* 模式二 先获取读在获取写
*/
public static void readWrite() {
readLock.lock();
System.out.println("获取到读锁");
writeLock.lock();
System.out.println("获取到写锁");
writeLock.unlock();
System.out.println("释放写锁");
readLock.unlock();
System.out.println("释放读锁");
}
上面两种模式中模式一是可以的,又叫锁降级,但模式二是不可以模式二会找成死锁。当然读写锁还一些API可以用来处理中断和超时和Reentrant相差不大。
三 总结
- 适合读多写少的场景。
- 利用共享模式实现读锁,独占模式实现写锁;
- 支持公平和非公平
- 写锁阻塞写锁和读锁,读锁阻塞写锁;