ReentrantLock使用

可重入

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。
相对于synchronized,它具备如下特点

  • 可中断
  • 可以设置超时时间
  • 支持多个条件变量
  • 可以设置为公平锁(默认不公平)

与synchronized一样,都支持可重入

基础语法:
image.png

条件变量

利用Condition对象,我们就可以让线程在合适的时间等待,或者在某一个特定的时刻得到通知,继续执行。
Condition接口提供的基本方法如下:
image.png

  • await,等待同时释放锁,当其他线程使用signal或signalAll方法才能重新运行

ReentrantLock原理

image.png

非公平锁实现原理

ReentantLock默认就是非公平锁实现的。

Lock()

  1. 当竞争出现时,尝试CAS改变state的值,失败
  2. 进入 tryAcquire 逻辑,这时 state 已经是1,结果仍然失败
  3. 接下来来进入 addWaiter 逻辑,构造 Node 队列
    • 图中黄色三角表示该 Node 的 waitStatus 状态,其中 0 为默认正常状态
    • 其中第一个 Node 称为 Dummy(哑元)或哨兵,用来占位,并不关联线程

image.png

然后线程进入 acquireQueued 逻辑(acquireQueued 会在一个死循环中不断尝试获得锁,失败后进入 park 阻塞)

当有多个线程同时竞争时,会一起阻塞住,阻塞后会变成下图所示:
image.png

  • Node上面的-1表示将由前继节点将“我”唤醒

UnLock()

Thread-0 释放锁,进入 tryRelease 流程,如果成功

  • 设置 exclusiveOwnerThread 为 null
  • state = 0

如果当前队列不为 null,并且 head 的 waitStatus = -1,进入 unparkSuccessor 流程
image.png

  • 会将离 head 最近的一个 Node使用unpark使其运转

可重入原理

继承自Sync的方法,加锁或解锁时判断其state的值是不是0,对其进行加减一

image.png

  • 当加锁时,会有monitorenter和monitorexit,执行monitorenter时,如果state为零,代表没有被其他线程持有,这时候Java虚拟机将该锁对象的持有线程设置为当前线程,计数器+1

image.png

  • state-1,当值为0时,锁才会真正释放。

可打断原理

默认是不可打断的,在此模式下,即使它被打断,仍会驻留在 AQS 队列中,一直要等到获得锁后方能得知自己被打断了。

image.png

可打断模式就是在循环的时候直接抛出异常:
image.png


公平锁的实现

与非公平锁主要区别在于 tryAcquire 方法的实现,进行竞争之前,会去检查AQS 队列中是否有前驱节点, 没有才去竞争