ReentrantReadWriteLoc读写锁-锁降级及锁升级问题

读写锁维护一对锁,一个读锁一个写锁,通过分离读锁和写锁使得并发性比一般的排他锁有了很大的提升。在没有读写锁支持的时候(Java5之前),完成读写操作需要使用Java的等待通知机制,也就是当写操作开始的时候,所有晚于写操作的读操作都会进入等待状态。只有当写操作完成并通知后,等待的读操作才能继续执行(写操作依赖synchronized进行同步)。改用读写锁之后,只需要在读操作时获取读锁,写操作时获取写锁即可。后续的读写操作都会被阻塞,写操作释放以后,所有操作继续执行。相比较于等待通知机制,编程方式变得更加简明。

读写锁支持公平性选择;重进入;锁降级。

锁降级

为什么要锁降级?

由于对于数据比较敏感, 线程需要在对数据修改以后, 获取到修改后的值, 并进行接下来的其它操作。后面我们对于 data 仅仅是读取。如果还一直使用写锁的话,就不能让多个线程同时来读取了,持有写锁是浪费资源的,降低了整体的效率,所以这个时候利用锁的降级是很好的办法,可以提高整体性能。

锁降级的实现

锁降级是把持住当前拥有的写锁,再获取到读锁,随后释放先前拥有的写锁。

锁降级中读锁的获取是否必要?

答案是必要的。主要是为了保证数据的可见性,若果当前线程不获取读锁而是直接释放写锁,假设此刻另一个线程T获取了写锁并修改了数据,那么当前线程无法感知线程T的数据更新(是说线程A使用数据时,并不知道别的线程已经更改了数据,所以使用的是线程T的修改结果。因此通过锁降级来保证数据每次修改后的可见性)。如果当前线程获取读锁,即遵循锁降级的步骤,则线程T将会被阻塞,直到当前线程使用数据并释放读锁后,线程T才能获取写锁进行数据更新。说白了就是,写锁和读锁的代码块中的操作是要保证串行执行,就相当于synchronized关键字修饰的一段代码块。

参考:

ReentrantReadWriteLock中锁降级的理解

锁升级

ReentrantReadWriteLock不支持锁升级(把持读锁、获取写锁,最后释放读锁的过程)。目的也是保证数据可见性,如果读锁已被多个线程获取,其中任意线程获取了写锁并更新了数据,则其更新对其他获取到的读锁的线程数不可见的。

我们知道读写锁的特点是如果线程都申请读锁,是可以多个线程同时持有的,可是如果是写锁,只能有一个线程持有,并且不可能存在读锁和写锁同时持有的情况。正是因为不可能有读锁和写锁同时持有的情况,所以升级写锁的过程中,需要等到所有的读锁都释放,此时才能进行升级。
假设有 A,B 和 C 三个线程,它们都已持有读锁。假设线程 A 尝试从读锁升级到写锁。那么它必须等待 B 和 C 释放掉已经获取到的读锁。如果随着时间推移,B 和 C 逐渐释放了它们的读锁,此时线程 A 确实是可以成功升级并获取写锁。但是我们考虑一种特殊情况。假设线程 A 和 B 都想升级到写锁,那么对于线程 A 而言,它需要等待其他所有线程,包括线程 B 在内释放读锁。而线程 B 也需要等待所有的线程,包括线程 A 释放读锁。这就是一种非常典型的死锁的情况。谁都愿不愿意率先释放掉自己手中的锁。
但是读写锁的升级并不是不可能的,也有可以实现的方案,如果我们保证每次只有一个线程可以升级,那么就可以保证线程安全。只不过最常见的 ReentrantReadWriteLock 对此并不支持。

参考:

面试官:读写锁了解吗?他的升降级啥时候用?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值