1、前言
在《从源码分析ReentrantLock原理》这一篇文章中分析了以非阻塞同步算法为基础实现的可重入独占锁ReentrantLock。所谓** “独占” 即同一时间只能有一个线程持有锁。而 “重入” **是指该线程如果持有锁,可以在同步代码块内再次请求占有锁而不被阻塞,线程重入后将AQS内部状态state同步加1继续同步区的操作。但是要注意该线程要想移交锁的控制权必须完全释放重入锁,即将AQS的state同步更新到0为止。ReentrantReadWriteLock出现的目的就是针对ReentrantLock独占带来的性能问题,使用ReentrantLock无论是“写/写”线程、“读/读”线程、“读/写”线程之间的工作都是互斥,同时只有一个线程能进入同步区域。然而大多实际场景是“读/读”线程间并不存在互斥关系,只有"读/写"线程或"写/写"线程间的操作需要互斥的。因此引入ReentrantReadWriteLock,它的特性是:一个资源可以被多个读操作访问,或者一个写操作访问,但两者不能同时进行,从而提高读操作的吞吐量。
2、读写锁简介
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。针对这种场景,JAVA的并发包提供了读写锁ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁,描述如下:
线程进入读锁的前提条件:
- 没有其他线程的写锁,
- 没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。
线程进入写锁的前提条件:
- 没有其他线程的读锁
- 没有其他线程的写锁
而读写锁有以下三个重要的特性:
- (1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
- (2)重进入:读锁和写锁都支持线程重进入。
- (3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。
3、初识ReentrantReadWriteLock
ReentrantReadWriteLock并没有继承ReentrantLock,也并没有实现Lock接口,而是实现了ReadWriteLock接口,该接口提供readLock()方法获取读锁,writeLock()获取写锁。