1. 流程图
1.1 加锁流程
1.2 解锁流程
2. 原理说明
2.1 加锁流程
重入锁加锁流程(非公平锁)
- 直接通过cas获取锁,获取成功设置当前线程独占锁;
- cas获取失败,通过acquire(1)方法获取;
- 逻辑判断boolean t=!tryAcquire(1)&&acquireQueued(addWaiter(Node.EXCLUSIVE), 1)
- 调用tryAcquire方法,接着调用nonfairTryAcquire方法;
- 判断方式,同步标记state,state==0?(cas获取锁成功?true:false):(当前线程持有锁?重入->true:false);
- 通过acquireQueued竞争锁
- addWaiter封装为exclusive类型节点node,然后加入到双向链表尾部
- 获取node前一个节点p,如果p为head并且tryAcquire成功,设置node为head
- 如果上一步失败,判断当前线程是否应该中断并中断
- 判断逻辑:节点p的状态ws:如果ws=-1,则需要中断;ws>0,跳过p判断它前一个,直到找到一个ws<=0的节点,然后丢弃中间的节点;否则,cas将当前节点状态设为-1,不中断;
- 中断方式: LockSupport.park(this)
- 调用tryAcquire方法,接着调用nonfairTryAcquire方法;
- 如果 t==true表示尝试之后也没有获取到锁, 中断当前线程,Thread.currentThread().interrupt();
2.2 解锁流程
调用release(1)来释放锁,实际调用tryRelease(1)
tryRelease: 获取state,重入次数减1,如果结果为0,则将排它锁线程设置为null,返回true
如果第2步返回true,则使用unparkSuccessor唤醒下一个waitStatus<0的节点;
在调用之前需判断head节点存在,且waitStatus!=0
3. 参数说明
3.1 state
- 当 state=0 时,表示无锁状态;
- 当 state>0 时,表示已经有线程获得了锁,也就是 state=1,但是因为ReentrantLock 允许重入,所以同一个线程多次获得同步锁的时候,state 会递增,比如重入 5 次,那么 state=5。 而在释放锁的时候,同样需要释放 5 次直到 state=0,其他线程才有资格获得锁。
3.2 waitStatus
Node 有 5 中状态,分别是:CANCELLED(1),SIGNAL(-1)、CONDITION(-2)、PROPAGATE(-3)、默认状态(0)
- CANCELLED: 在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该 Node 的结点, 其结点的 waitStatus 为 CANCELLED,即结束状态,进入该状态后的结点将不会再变化;
- SIGNAL: 只要前置节点释放锁,就会通知标识为 SIGNAL 状态的后续节点的线程;
- CONDITION: 和 Condition 有关系,后续会讲解;
- PROPAGATE:共享模式下,PROPAGATE 状态的线程处于可运行状态;
- 0:初始状态