ReentrantLock使用
可重入
可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。
相对于synchronized,它具备如下特点
- 可中断
- 可以设置超时时间
- 支持多个条件变量
- 可以设置为公平锁(默认不公平)
与synchronized一样,都支持可重入
基础语法:
条件变量
利用Condition对象,我们就可以让线程在合适的时间等待,或者在某一个特定的时刻得到通知,继续执行。
Condition接口提供的基本方法如下:
- await,等待同时释放锁,当其他线程使用signal或signalAll方法才能重新运行
ReentrantLock原理
非公平锁实现原理
ReentantLock默认就是非公平锁实现的。
Lock()
- 当竞争出现时,尝试CAS改变state的值,失败
- 进入 tryAcquire 逻辑,这时 state 已经是1,结果仍然失败
- 接下来来进入 addWaiter 逻辑,构造 Node 队列
- 图中黄色三角表示该 Node 的 waitStatus 状态,其中 0 为默认正常状态
- 其中第一个 Node 称为 Dummy(哑元)或哨兵,用来占位,并不关联线程
然后线程进入 acquireQueued 逻辑(acquireQueued 会在一个死循环中不断尝试获得锁,失败后进入 park 阻塞)
当有多个线程同时竞争时,会一起阻塞住,阻塞后会变成下图所示:
- Node上面的-1表示将由前继节点将“我”唤醒
UnLock()
Thread-0 释放锁,进入 tryRelease 流程,如果成功
- 设置 exclusiveOwnerThread 为 null
- state = 0
如果当前队列不为 null,并且 head 的 waitStatus = -1,进入 unparkSuccessor 流程
- 会将离 head 最近的一个 Node使用unpark使其运转
可重入原理
继承自Sync的方法,加锁或解锁时判断其state的值是不是0,对其进行加减一
- 当加锁时,会有monitorenter和monitorexit,执行monitorenter时,如果state为零,代表没有被其他线程持有,这时候Java虚拟机将该锁对象的持有线程设置为当前线程,计数器+1
- state-1,当值为0时,锁才会真正释放。
可打断原理
默认是不可打断的,在此模式下,即使它被打断,仍会驻留在 AQS 队列中,一直要等到获得锁后方能得知自己被打断了。
可打断模式就是在循环的时候直接抛出异常:
公平锁的实现
与非公平锁主要区别在于 tryAcquire 方法的实现,进行竞争之前,会去检查AQS 队列中是否有前驱节点, 没有才去竞争