设计:读写锁

本文分析了读写锁的概念,重点探讨了Java中的ReentrantReadWriteLock实现,包括其读写分离、重入特性以及锁降级。通过分析synchronized和AQS框架下的读写锁实现,阐述了读写锁的设计思想和防止锁升级以避免死锁的重要性。
摘要由CSDN通过智能技术生成

分析reentrantReadWriteLock

读写锁具有读写分离的思想,读锁是一个共享锁,而写锁是一个互斥锁。线程如果要读某个资源那么便对该资源加读锁,一个资源可以被多个线程加读锁,而一个线程如果需要对某个资源进行写操作便需要加写锁,如果该资源上已经被其他线程加入了写锁,便不可以被加读锁。

加锁本质上就是修改一个变量,其他线程读取一个被加锁的共享资源时需要先查看锁变量释放被人占用。加锁成功就是成功对变量进行了相应修改(置位或者自增),而加锁失败则会自旋或阻塞,直到锁变量被重置/复位/释放。读锁写锁的释放,说白了就是相应的读状态/写状态自增自减罢了。

这里讨论的是一个抽象的读写锁实现思想,不同的产品有具体的实现细节(如mysql锁、java锁等),一般情况下,单个线程对某个资源加上写锁,那么它便宣称了对资源的独占,因此写写、写读重入都是允许的,而某个线程仅是对资源加了读锁,那么无法做到读写重入,写操作 将被阻塞。

互斥锁中,我们没有获得锁就不能读或写变量,获取锁失败的线程会被阻塞。而现在引入读写锁后,访问变量的操作被细分为读和写,A线程和B线程都是读操作,因此它们可以获得共享锁后一起访问变量,而A线程和B线程都是写操作(至少有一个写操作),则重新退化为互斥锁。其中不变的是,访问变量必须先获得锁,没有获得锁的变量被阻塞在同步代码之外,只不过获得的锁可以分为共享锁和互斥锁

java中读写锁的实现就是ReentrantReadWriteLock,并且实现了ReadWriteLock接口、内部通过成员Syn依赖AQS框架,两个内部类WriteLock和readLock分别实现了实现了Lock接口,而ReadWriteLock接口对应的readLock和writeLock方法返回的就是两个内部类WriteLock和readLock的实例对象。

    public ReentrantReadWriteLock.WriteLock writeLock() {
    return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  {
    return readerLock; }

而ReentrantReadWriteLock的锁变量使用的仍然是AQS维护的同步状态state,这个state是int类型,高16位用于表示读状态,低16位用于表示写状态。其中是否处于上锁状态直接看相应的低位即可(通过掩码和移位操作),而更高的位代表重入次数。
其中读状态是所有线程获取读锁次数的总和,每个线程获取读锁的次数存在于threadLocal中,而写状态记录的是owner的重人次数,其中写锁获得者通过变量标识,读写锁能够被分别获取的次数不超过2的16次方。

写操作需要被其他线程的读操作可见,而存在读操作时不可以获取写锁,这本质上就是为了避免读写冲突。(mysql基于MVCC可以实现非锁定读并避免读写冲突,而ReentrantReadWriteLock中的读写都是同步的,因此读写操作不能同时进行)

ReentrantReadWriteLock支持锁降级,如果有一段代码,先是少量的写然后是大量的读,那么写锁的释放就不必等待整个代码全部执行完毕,而是执行完写的代码后就可以释放写锁——当前线程先保持写锁,然后拿到读锁,释放写锁。(它和普通的申请锁操作不同的是,它可以在不释放写锁的前提下,申请读锁)

ReentrantReadWriteLock不支持锁升级——先拿到读再拿到写,再放掉读。因为锁升级存在死锁风险:A和B如果都想锁升级,那么他们就必须等待对方的读锁释放,但是它们自己又不主动释放读锁,这满足死锁条件——线程持有互斥的写锁,不主动放弃,等待对方释放读锁,并且当前线程与对方线程处于循环等待的状态
这导致两个线程进行僵死状态而不能向下推进。(我们只能不持有任何锁的前提下,去申请写锁)

设计读写锁

面试中设计读写锁的题目时常有,但是通常要求不算严格,重要的是体现读写分离以及锁分离的思想,即怎么对一把锁进行封装,达成两种不同类型锁的效果。

synchronized实现

之前说过,锁本质上就是变量,这一点在原生实现上体现的更是深刻。

        ReadWriteLock lock = new ReadWriteLock()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值