ReentrantReadWriteLock锁的使用

本文详细介绍了ReentrantReadWriteLock的概念及使用方式,包括其特点、读写锁的区别、使用注意事项等,并通过实例演示如何正确使用读写锁避免死锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.什么是ReentrantReadWriteLock

    ReentrantReadWriteLock是jdk提供的一个可重入的读写锁,功能与synchronized类似

    什么叫可重入:就是同一个线程可以重复加锁,可以对同一个锁加多次,每次释放的时候回释放一次,直到该线程加锁次数为0,这个线程才释放锁。

    什么叫读写锁: 也就是读锁可以共享,多个线程可以同时拥有读锁,但是写锁却只能只有一个线程拥有,而且获取写锁的时候,其他线程都已经释放了读锁,而且在该线程获取写锁之后,其他线程不能再获取读锁。

    可重入环境举例:递归调用

    ReentrantReadWriteLock并没有继承ReentrantLock,也并没有实现Lock接口!

2.ReentrantReadWriteLock的锁

    ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁

    读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,只要上好相应的锁即可。如果代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;

    如果代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

1、读和读之间不互斥,因为读操作不会有线程安全问题

2、写和写之间互斥,避免一个写操作影响另外一个写操作,引发线程安全问题

3、读和写之间互斥,避免读操作的时候写操作修改了内容,引发线程安全问题

    总结起来就是,多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作

但是:

  1. 先读再写的情况,请先释放读锁,再上写锁,ReentrantReadWriteLock不支持读锁升级为写锁
  2. 先写再读的情况,可以在上写锁的情况,再上读锁,解锁的时候先解读锁再解写锁

3.ReentrantReadWriteLock的使用

(1)创建锁

165150_mG1v_2248830.png

(2)单独读写代码使用

readLock.lock() 表示获得读锁

165208_ixNm_2248830.png

其中,红色框框部分需要注意:

lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能得不到释放,读锁一直未释放的情况下,由于读写锁是互斥的,这个时候如果新的线程想

去写,就无法写入....

同样,writeLock的用法一样:

165229_lhb0_2248830.png

同样也需要在finally中进行释放锁,原因同上

  (3)读写混合情况

    ①先读再写

            务必先释放读锁,再上写锁,否则会造成死锁

    先看下面这段代码:

165255_fMHq_2248830.png

先上了readLock,然后再上writeLock,这时候,会一直死锁,下面的 get write lock 也不会输出

165309_WzU4_2248830.png

修改一下上面的代码,把readLock.unlock()放到writeLock.lock()之前:

165323_V4M6_2248830.png

重新运行代码,就正常输出了:

165347_XRM3_2248830.png

    ②先写再读

    由于ReentrantReadWriteLock支持锁降级,因此这段代码不会造成死锁

165403_SGnF_2248830.png

 

4.ReentrantReadWriteLock获取锁和释放锁的注意点:

1.一个线程获得了多少次锁,就需要解多少次的锁,如果线程获得了两个读锁,但是只解了一次读锁,会造成死锁

2.一个线程未获得该锁,但是执行了解锁,会造成 IllegalMonitorStateException

在代码优化过程中,正好遇到上述问题:

165422_e1wF_2248830.png

一个thread在调用get(Object o)的时候,先去判断时间间隔,是否满足刷新条件,如果满足了条件,则把readLock释放了(reload方法中有writeLock),在reload()执行完毕后, 再去get.

如果不满足刷新条件,则直接return get方法,这个时候,就会出现readLock未解锁的情况,就导致了死锁,其他线程无法更新数据.

如果在catch后面新增 finally:

165439_5Akb_2248830.png

则可以解决死锁问题,但是新的问题来了.....如果进了if判断后的逻辑,readLock先被释放了一遍,最后finally又释放了一遍,这时候,代码运行就会报错(编译不报错),提示java.lang.IllegalMonitorStateException

也就是上面的说的第二点

解决方案:在reload()之后重新上锁,对应finally中的解锁:

165456_WmjO_2248830.png

这样,上锁解锁就一一对应了,既不会造成死锁,也不会出现IllegalMonitorStateException

 

总结一下

ReentrantReadWriteLock

  1. 利用共享模式实现读锁,独占模式实现写锁;
  2. 支持公平和非公平,非公平的情况下可能会出现读锁阻塞写锁的场景;
  3. 写锁阻塞写锁和读锁,读锁阻塞写锁;
  4. 写锁可以降级为读锁,读锁不能升级为写锁,只能先release再lock;
  5. 写锁支持condition条件;
  6. 读写锁都支持超时/中断lock;
  7. 适合读多写少的场景。

 

 

本文参考链接:

http://www.jianshu.com/p/9f98299a17a5 

http://blog.youkuaiyun.com/prestigeding/article/details/53286756

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值