【源码分析-JDK】-1.ReentrantLock加锁和解锁


1. 流程图

1.1 加锁流程

1-1.ReentrantLock加锁流程图-new

1.2 解锁流程

2.ReentrantLock解锁流程

2. 原理说明

2.1 加锁流程

重入锁加锁流程(非公平锁)

  1. 直接通过cas获取锁,获取成功设置当前线程独占锁;
  2. cas获取失败,通过acquire(1)方法获取;
  3. 逻辑判断boolean t=!tryAcquire(1)&&acquireQueued(addWaiter(Node.EXCLUSIVE), 1)
    1. 调用tryAcquire方法,接着调用nonfairTryAcquire方法;
      1. 判断方式,同步标记state,state==0?(cas获取锁成功?true:false):(当前线程持有锁?重入->true:false);
    2. 通过acquireQueued竞争锁
      1. addWaiter封装为exclusive类型节点node,然后加入到双向链表尾部
      2. 获取node前一个节点p,如果p为head并且tryAcquire成功,设置node为head
      3. 如果上一步失败,判断当前线程是否应该中断并中断
        1. 判断逻辑:节点p的状态ws:如果ws=-1,则需要中断;ws>0,跳过p判断它前一个,直到找到一个ws<=0的节点,然后丢弃中间的节点;否则,cas将当前节点状态设为-1,不中断;
        2. 中断方式: LockSupport.park(this)
  4. 如果 t==true表示尝试之后也没有获取到锁, 中断当前线程,Thread.currentThread().interrupt();

2.2 解锁流程

  1. 调用release(1)来释放锁,实际调用tryRelease(1)

  2. tryRelease: 获取state,重入次数减1,如果结果为0,则将排它锁线程设置为null,返回true

  3. 如果第2步返回true,则使用unparkSuccessor唤醒下一个waitStatus<0的节点;

  4. 在调用之前需判断head节点存在,且waitStatus!=0

3. 参数说明

3.1 state

  1. 当 state=0 时,表示无锁状态;
  2. 当 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:初始状态

文章作者: Soulballad
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Soulballad !
评论
 上一篇
【源码分析-JDK】-2.线程池工作原理 【源码分析-JDK】-2.线程池工作原理
1. 线程池的类型 newFixedThreadPool:该方法返回一个固定数量的线程池,线程数不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行; newSingleThrea
2021-04-12
下一篇 
【源码分析-Mybatis】-1.Mybatis核心原理 【源码分析-Mybatis】-1.Mybatis核心原理
1. 创建会话工厂 2. 创建会话 3. 获取代理对象 4. 执行SQL 5. 插件原理 6. 连接池
2021-04-08
  目录