目录
前言
在没有读写锁之前,ReentrantLock
和 Synchronized
虽然可以保证线程安全,但是也浪费了一定的资源
- 因为如果多个读操作同时进行,其实并没有线程安全问题,可以允许让多个读操作并行,以便提高程序效率
- 但是写操作不是线程安全的,如果多个线程同时写,或者在写的同时进行读操作,便会造成线程安全问题
我们的 ReadWriteLock
接口就解决了这样的问题,它设定了一套规则,既可以保证多个线程同时读的效率,同时又可以保证有写入操作时的线程安全
整体思路是它有两把锁:一把锁是写锁,获得写锁之后,既可以读数据又可以修改数据,而另一把锁是读锁,获得读锁之后,只能查看数据,不能修改数据。读锁可以被多个线程同时持有,所以多个线程可以同时查看数据。在读的地方合理使用读锁,在写的地方合理使用写锁,灵活控制,可以提高程序的执行效率
ReadWriteLock
接口简介及源码
ReadWriteLock
是 java.util.concurrent.locks
包下的接口。ReadWriteLock
管理一组锁,一个是读锁,一个是写锁
- 读锁是共享的
- 写锁是独占的
- 理论上,读写锁比互斥锁(
Synchronized
和ReentrantLock
)有更好的性能体现
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
所有 ReadWriteLock
接口的实现类必须保证内存同步效果:所有写锁 writeLock
的相关操作都对只读锁 readLock
可见。也就是说,如果一个线程成功的获取了只读锁 readLock
,那么这个线程可以看到上个写锁 writeLock
所做的所有修改
ReetrantReadWriteLock
类
ReentrantReadWriteLock
被称为读写锁,它是 ReadWriteLock
接口的实现
获取顺序
这个类不会强行指定访问锁的读写顺序,但是它支持一个可选的公平策略
非公平模式(默认)
当以非公平模式初始化时,读锁和写锁的获取的顺序是不确定的。非公平锁主张竞争获取,可能会延缓一个或多个读或写线程,但是会比公平锁有更高的吞吐量
公平模式
- 当以公平模式初始化时,线程将会以队列的顺序获取锁。当前线程释放锁后,等待时间最长的写锁线程就会被分配写锁;或者有一组读线程组等待时间比写线程长,那么这组读线程组将会被分配读锁
- 当有写线程持有写锁或者有等待的写线程时,一个尝试获取公平的读锁(非重入)的线程就会阻塞。这个线程直到等待时间最长的写锁获得锁后并释放掉锁后才能获取到读锁
支持 Condition
就像 ReentrantLock
一样,写锁支持 Condition
操作。当然,这种 Condition
操作,只能被应用在写锁上。读锁不支持 Condition
操作,readLock().newCondition()
会抛出一个 UnsupportedOperationException
异常
ReentrantReadWriteLock
源码
ReentrantReadWriteLock
主要源码
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
private final ReentrantReadWriteLock.ReadLock readerLock;
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
public ReentrantReadWriteLock() {
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
// 实现了接口 ReadWriteLock 的 writeLock() 方法
public ReentrantReadWriteLock.WriteLock writeLock() {
return writerLock;
}
// 实现了接口 ReadWriteLock 的 readLock() 方法
public ReentrantReadWriteLock.ReadLock readLock() {
return readerLock;
}
// 返回当前线程获取写锁的次数
public int getWriteHoldCount() {
return sync.getWriteHoldCount();
}
// 返回当前线程获取读锁的次数
public int getReadHoldCount() {
return sync.getReadHoldCount();
}
// 返回当前读锁被获取的次数,但不是占用该锁的线程数,也就是说,一个线程如果 n 次获取该锁,该方法返回 n,而不是 1
public int getReadLockCount() {
return sync.getReadLockCount();
}
// 判断写锁是否被获取
public boolean isWriteLocked() {
return sync.isWriteLocked();
}
// 静态内部类 ReadLock
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;
}
// 实现了接口 Lock 的方法
public void lock() {
sync.acquireShared(1);
}
// 实现了接口 Lock 的方法
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 实现了接口 Lock 的方法
public boolean tryLock() {
return sync.tryReadLock();
}
// 实现了接口 Lock 的方法
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
// 实现了接口 Lock 的方法
public void unlock() {
sync.releaseShared(1);
}
// 实现了接口 Lock 的方法
public Condition newCondition() {
throw new UnsupportedOperationException();
}
// 重写了父类 Object 类的方法
public String toString() {
int r = sync.getReadLockCount();
return super.toString() +
"[Read locks = " + r + "]";
}
}
// 静态内部类 WriteLock
public static class WriteLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -4992448646407690164L;
private final Sync sync;
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() {
sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock( ) {
return sync.tryWriteLock();
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
// 重写了父类 Object 类的方法
public String toString() {
Thread o = sync.getOwner();
return super.toString() + ((o == null) ?
"[Unlocked]" :
"[Locked by thread " + o.getName() + "]");
}
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
public int getHoldCount() {
return sync.getWriteHoldCount();
}
}
}
ReentrantReadWriteLock
获取读锁或写锁的方法
// 实现了接口 ReadWriteLock 的 writeLock() 方法
public ReentrantReadWriteLock.WriteLock writeLock() {
return writerLock;
}
// 实现了接口 ReadWriteLock 的 readLock() 方法
public ReentrantReadWriteLock.ReadLock readLock() {
return readerLock;
}
ReentrantReadWriteLock
获取与释放锁
读锁的获取
// 拿到 ReentrantReadWriteLock 的实例
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 使用 ReentrantReadWriteLock 的实例,调用自己的 readLock() 方法拿到读锁
Lock readLock = readWriteLock.readLock();
读锁的释放
readLock.unlock();
写锁的获取
// 拿到 ReentrantReadWriteLock 的实例
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 使用 ReentrantReadWriteLock 的实例,调用自己的 writeLock() 方法拿到写锁
Lock writeLock = readWriteLock.writeLock();
写锁的释放
readLock.unlock();
ReetrantReadWriteLock
使用场景
适用于读多写少的场景,在此场景中能发挥它的优势。提高了效率的同时,又可以保证线程的安全