从 JDK1.5 开始,JDK 加入了 ReentrantLock 类,该类能够像 synchronized 关键字一样实现线程之间的同步互斥,并且在功能上比 synchronized 更强大,使用起来也更灵活。
然而,ReentrantLock 在某些情况下也有自身的不足,因为同一时间内只有一个线程能够获取到锁并执行,这虽然保证了线程的安全,但假设我们有多个线程只是需要对对象进行读取操作,并没有写操作,这个时候怎么办?用 ReentrantLock 上锁让线程们排队一个个读取吗?这样子效率就太低了,很扯,读取应该是大家都能一起读取。那直接不上锁怎么样呢?这确实可以让多个线程同时读取,但这也不实际,假设在众多的线程里面,有那么几个是要进行写操作的,这可怎么办?
我们上面假设的业务场景中,需要让线程可以同时进行读取,但是在写的过程中其他线程无法进行读取,于是,JKD 提供了读写锁——ReentrantReadWriteLock,在上面的场景中,我们可以使用 ReentrantReadWriteLock 提高代码的执行效率、同时也兼顾线程安全问题。
读写锁——ReentrantReadWriteLock
顾名思义,读写锁包含了两种类型的锁,一个是读锁(ReadLock),与读操作相关,也称作共享锁;另外一个是写锁(WriteLock),与写操作相关,也叫排他锁。
ReadLock 与 WriteLock 的关系如下:
- ReadLock 与 ReadLock 不互斥
- ReadLock 与 WriteLock 互斥
- WriteLock 与 WriteLock 互斥
也即是,当没有线程获取到写锁的时候,同一时刻可以有多个线程可以获取到读锁并进行读操作;
当有线程获取到读锁的时候,其他线程只能获取读锁,没法获取写锁,需要等所有读锁都释放了,其他线程才能获取到写锁并进行写操作;
当有线程获取到写锁的时候,其他线程既不能获取读锁,也不能获取写锁,需要等写锁被释放了,其他线程才能获取读锁或者写锁;
实例演示
示例一:读锁与读锁之间不互斥
编写一个模拟读写的业务类 ReadWriteService,ReadWriteService 模拟了读操作和写操作,在读写完毕后释放对应的锁;
package com.shawearn.test.service;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 读写业务类;
* <p>
* author: shawearn
* date: 2018-04-11 00:25
*/
public class ReadWriteService {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 模拟读写操作所需消耗的时间,单位:毫秒;
private final long sleepTime = 10000l;
/**
* 获取读锁;
*/
public void read() {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName()
+ " 获取读锁 " + System.currentTimeMillis());
// 线程阻塞 10000 毫秒,用以证明锁之间是否互斥;
Thread.sleep(sleepTime);
// 释放读锁;
System.out.println(Thread.currentThread().getName()
+ " 读完成,释放读锁 " + System.currentTimeMillis());
lock.readLock().unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 获取写锁;
*/
public void write() {
try {
lock.writeLock().lock(