ReentrantReadWriteLock顾名思义是可重入的读写锁,允许多个读线程获得ReadLock,但只允许一个写线程获得WriteLock。
注意:本文内容是基于jdk1.8.0_40的,不同jdk版本可能某些实现会有所修改。
原理介绍
1. ReentrantReadWriteLock类图:
2. ReentrantReadWriteLock内部定义了static abstrat Sync类,它继承自AQS。
3. ReentrantReadWriteLock提供的ReadLock是共享的,而WriteLock是独占的。于是Sync类同时实现了AQS中独占和共享模式的抽象方法(tryAcquire/tryAcquireShared等),用同一个等待队列来维护读/写排队线程,而用一个32位int state标示和记录读/写锁重入次数--这里要实现读锁和写锁,只有一个状态怎么办?Doug Lea是这么做的,它把状态的高16位用作读锁,记录所有读锁重入次数之和,低16位用作写锁,记录写锁重入次数。所以无论是读锁还是写锁最多只能被持有65535次。
4. FairSync和NonfairSync继承自Sync类,实现了公平/非公平策略。
ReentrantReadWriteLock特性
-
公平性
-
非公平锁(默认),为了防止写线程饿死,规则是:当等待队列头部结点是独占模式(即要获取写锁的线程)时,只有获取独占锁线程可以抢占,而试图获取共享锁的线程必须进入队列阻塞;当队列头部结点是共享模式(即要获取读锁的线程)时,试图获取独占和共享锁的线程都可以抢占。
-
公平锁,利用AQS的等待队列,线程按照FIFO的顺序获取锁,因此不存在写线程一直等待的问题。
-
- 重入性
- 读写锁均是可重入的,读/写锁重入次数保存在了32位int state的高/低16位中。而单个读线程的重入次数,则记录在ThreadLocalHoldCounter类型的readHolds里。
-
锁降级
-
写线程获取写入锁后可以获取读取锁,然后释放写入锁,这样就从写入锁变成了读取锁,从而实现锁降级。
-
-
锁获取中断
-
读取锁和写入锁都支持获取锁期间被中断。
-
-
条件变量
-
写锁提供了条件变量(Condition)的支持,这个和独占锁ReentrantLock一致,但是读锁却不允许,调用readLock().newCondition()会抛出
UnsupportedOperationException
异常。
-
相关文章链接
并发编程实践六:ReentrantReadWriteLock (原理和源码解析)