侧边栏壁纸
博主头像
soulballad博主等级

技术文章记录及总结

  • 累计撰写 169 篇文章
  • 累计创建 26 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

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

soulballad
2021-04-10 / 0 评论 / 0 点赞 / 63 阅读 / 1,253 字
温馨提示:
本文最后更新于 2022-04-19,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

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:初始状态
0

评论区