ReentrantLock的理解

ReentrantLock的实现

底层基于AQS。支持公平和非公平两种方式。

内部有一个state和两个队列。同步队列和等待队列。

通过CAS修改state来争抢锁。争抢不到则进入同步队列等待,同步队列是双向链表。

条件Condition不满足的时候进入等待队列等待,等待队列是单向链表。
ReentrantLock 本身不直接涉及到等待队列,但它通常与 Condition 配合使用,
后者会使用等待队列来管理处于等待状态的线程。

流程:
一个线程抢锁的时候,如果是公平锁,则看同步队列里面是否已经有线程在等待这把锁了,
有的话,直接放入同步队列的尾部等待;
如果不是公平锁,上来直接通过CAS修改state的值进行抢锁,修改成功则获取到锁,
修改不成功,则加入同步队列中,等待;
不满足Condition的线程会被加入到等待队列,等待被唤醒,唤醒之后会先通过CAS抢锁,
抢不到就会加入同步队列,排队等待锁。

抢锁的细节:
先检查state的值,state为0说明没有线程占有锁,会通过cas去设置state的值,成功就获得了锁,
同时将当前线程设置成占用这把锁的线程。

如果state不是0,说明锁被别的线程占着,然后判断一下是不是自己占着的,如果是自己的话,
则通过cas将state的值再叠加申请的数量。本质就是通过cas增加state的值。(<- ReentrantLock怎么实现可重入的?)

释放锁细节:
先判断释放锁的线程,是不是当前占有锁的线程,如果不是的话,直接抛异常

释放锁的函数会传进来一个int releases,state 会减去这个releases;
减去之后,如果state剩余的值是0,则说明锁要释放了,源码里面会将占有锁的线程设置为null,
同时通过cas将state的值改成0。如果state不是0,直接将state设置为新的值。

自旋锁:
轻量级的锁。线程获取锁失败的时候,不会立即进入阻塞状态,而是循环反复尝试获取锁。
优点:避免了线程上下文切换的开销。适合锁等待时间短的场景。
缺点:锁饥饿 + 性能问题
有可能某个线程长时间获取不到锁
cpu空转,消耗资源 + CAS总线风暴问题

CLH:基于队列的自旋锁
隐式队列:线程组织成一个队列进行排队,每个线程不是CAS争抢同一个变量,
而是CAS自旋监控它前面线程的状态(线程本地变量);如果前面的线程状态为释放了锁,
那么后序的线程则抢锁。
解决了:锁饥饿问题和CAS总线风暴问题(因为不是争抢同一个变量)
缺点:需要自旋,浪费cpu

实现: ThreadLocal + AtomicReference + 一个isLocked

AQS对CLH的改造:
CLH存在占用cpu资源的问题,AQS将自旋等待前置节点改成了阻塞等待,
改成阻塞等待之后,无法获取前面的线程的锁的状态,所以需要前面的线程
通知。所以显示的维护了一个双向队列。线程等待超时或者取消之后,需要从队列里面移除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值