1.什么是ReentrantReadWriteLock
ReentrantReadWriteLock是jdk提供的一个可重入的读写锁,功能与synchronized类似
什么叫可重入:就是同一个线程可以重复加锁,可以对同一个锁加多次,每次释放的时候回释放一次,直到该线程加锁次数为0,这个线程才释放锁。
什么叫读写锁: 也就是读锁可以共享,多个线程可以同时拥有读锁,但是写锁却只能只有一个线程拥有,而且获取写锁的时候,其他线程都已经释放了读锁,而且在该线程获取写锁之后,其他线程不能再获取读锁。
可重入环境举例:递归调用
ReentrantReadWriteLock并没有继承ReentrantLock,也并没有实现Lock接口!
2.ReentrantReadWriteLock的锁
ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁
读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,只要上好相应的锁即可。如果代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;
如果代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!
1、读和读之间不互斥,因为读操作不会有线程安全问题
2、写和写之间互斥,避免一个写操作影响另外一个写操作,引发线程安全问题
3、读和写之间互斥,避免读操作的时候写操作修改了内容,引发线程安全问题
总结起来就是,多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。
但是:
- 先读再写的情况,请先释放读锁,再上写锁,ReentrantReadWriteLock不支持读锁升级为写锁
- 先写再读的情况,可以在上写锁的情况,再上读锁,解锁的时候先解读锁再解写锁
3.ReentrantReadWriteLock的使用
(1)创建锁
(2)单独读写代码使用
readLock.lock() 表示获得读锁
其中,红色框框部分需要注意:
lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能得不到释放,读锁一直未释放的情况下,由于读写锁是互斥的,这个时候如果新的线程想
去写,就无法写入....
同样,writeLock的用法一样:
同样也需要在finally中进行释放锁,原因同上
(3)读写混合情况
①先读再写
务必先释放读锁,再上写锁,否则会造成死锁
先看下面这段代码:
先上了readLock,然后再上writeLock,这时候,会一直死锁,下面的 get write lock 也不会输出
修改一下上面的代码,把readLock.unlock()放到writeLock.lock()之前:
重新运行代码,就正常输出了:
②先写再读
由于ReentrantReadWriteLock支持锁降级,因此这段代码不会造成死锁
4.ReentrantReadWriteLock获取锁和释放锁的注意点:
1.一个线程获得了多少次锁,就需要解多少次的锁,如果线程获得了两个读锁,但是只解了一次读锁,会造成死锁
2.一个线程未获得该锁,但是执行了解锁,会造成 IllegalMonitorStateException
在代码优化过程中,正好遇到上述问题:
一个thread在调用get(Object o)的时候,先去判断时间间隔,是否满足刷新条件,如果满足了条件,则把readLock释放了(reload方法中有writeLock),在reload()执行完毕后, 再去get.
如果不满足刷新条件,则直接return get方法,这个时候,就会出现readLock未解锁的情况,就导致了死锁,其他线程无法更新数据.
如果在catch后面新增 finally:
则可以解决死锁问题,但是新的问题来了.....如果进了if判断后的逻辑,readLock先被释放了一遍,最后finally又释放了一遍,这时候,代码运行就会报错(编译不报错),提示java.lang.IllegalMonitorStateException
也就是上面的说的第二点
解决方案:在reload()之后重新上锁,对应finally中的解锁:
这样,上锁解锁就一一对应了,既不会造成死锁,也不会出现IllegalMonitorStateException
总结一下
ReentrantReadWriteLock
- 利用共享模式实现读锁,独占模式实现写锁;
- 支持公平和非公平,非公平的情况下可能会出现读锁阻塞写锁的场景;
- 写锁阻塞写锁和读锁,读锁阻塞写锁;
- 写锁可以降级为读锁,读锁不能升级为写锁,只能先release再lock;
- 写锁支持condition条件;
- 读写锁都支持超时/中断lock;
- 适合读多写少的场景。
本文参考链接:
http://www.jianshu.com/p/9f98299a17a5
http://blog.youkuaiyun.com/prestigeding/article/details/53286756