文章目录

  • 无线程加锁时的加锁方法:
  • 有竞争时的线程加锁方法
  • 加入阻塞队列的方法
  • 释放锁(不公平):
  • 可重入原理:
  • 可打断原理:
  • 公平锁实现:
  • 条件变量实现(await)
  • 条件变量(sigal)

构造器:
使用的锁:
有两种实现,公平锁和非公平锁

abstract static class Sync extends AbstractQueuedSynchronizer {

默认是非公平锁的实现

    public ReentrantLock() {sync = new NonfairSync();}

无线程加锁时的加锁方法:


final void lock() {if (compareAndSetState(0, 1))//尝试加锁setExclusiveOwnerThread(Thread.currentThread());//成功后就将当前线程改为自己else//失败acquire(1);}

有竞争时的线程加锁方法

 public final void acquire(int arg) {if (!tryAcquire(arg) &&//在此重试加锁acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//尝试加入阻塞队列selfInterrupt();}


Thread-1 执行了

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

加入阻塞队列的方法

    final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {//前驱是头结点(由于虚拟节点的存在),就再次尝试一次获取锁setHead(node);//设为头结点p.next = null; // help GCfailed = false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) &&//这里要循环两次(详见下方)parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

当前线程进入 acquireQueued 逻辑

  1. acquireQueued 会在一个死循环中不断尝试获得锁,失败后进入 park 阻塞
  2. 如果自己是紧邻着 head(排第二位),那么再次 tryAcquire 尝试获取锁,当然这时 state 仍为 1,失败
  3. 进入 shouldParkAfterFailedAcquire 逻辑,将前驱 node,即 head 的 waitStatus 改为 -1,这次返回 false(改为-1,意味着他有责任唤醒我,这是第一次循环)

  1. shouldParkAfterFailedAcquire 执行完毕回到 acquireQueued ,再次 tryAcquire 尝试获取锁,当然这时 state 仍为 1,失败
  2. 当再次进入 shouldParkAfterFailedAcquire 时,这时因为其前驱 node 的 waitStatus 已经是 -1,这次返回 true (第二次循环,那么就真的要阻塞了)
  3. 进入 parkAndCheckInterrupt, Thread-1 park(灰色表示)
 private final boolean parkAndCheckInterrupt() {LockSupport.park(this);//暂停当前线程return Thread.interrupted();}

释放锁(不公平):

首先看释放锁的方法

public void unlock() {sync.release(1);}
    public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)//头结点不为空且需要唤醒下一个节点unparkSuccessor(h);//恢复阻塞的线程运行,阻塞的代码在上面的第二次循环**acquireQueued** 处。。。再一次进入for时,如果没有和他竞争的线程,就可以获取锁了,并退出阻塞方法的循环return true;}return false;}protected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}

获取锁不公平的体现:

  • 当前队列不为 null,并且 head 的 waitStatus = -1,进入 unparkSuccessor 流-程
  • 找到队列中离 head 近的一个 Node(没取消的),unpark 恢复其运行,本例中即为 Thread-1
  • 回到 Thread-1 的 acquireQueued 流程


如果加锁成功(没有竞争),会设置

  • exclusiveOwnerThread 为 Thread-1,state = 1
  • head 指向刚刚 Thread-1 所在的 Node,该 Node 清空 Thread
  • 原本的 head 因为从链表断开,而可被垃圾回收

不公平来了)如果这时候有其它线程来竞争(非公平的体现),例如这时有 Thread-4 来了

如果不巧又被 Thread-4 占了先

  • Thread-4 被设置为 exclusiveOwnerThread,state = 1
  • Thread-1 再次进入 acquireQueued 流程,获取锁失败,重新进入 park 阻塞

可重入原理:

再次回顾一下非公平锁的加锁方法:

     final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}

我们详细了解nonfairTryAcquire方法:

final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();// 如果还没有获得锁if (c == 0) {// 尝试用 cas 获得, 这里体现了非公平性: 不去检查 AQS 队列if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}// 如果已经获得了锁, 线程还是当前线程, 表示发生了锁重入 else if (current == getExclusiveOwnerThread()) {// state++int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}// 获取失败, 回到调用处 return false;}

释放锁时会有相应判断:

        protected final boolean tryRelease(int releases) {// state-- int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;// 支持锁重入, 只有 state 减为 0, 才释放成功if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}

可打断原理:

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

 private final boolean parkAndCheckInterrupt() {// 如果打断标记已经是 true, 则 park 会失效  LockSupport.park(this);        // interrupted 会清除打断标记        return Thread.interrupted();
} final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;// 还是需要获得锁后, 才能返回打断状态 return interrupted;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())// 如果是因为 interrupt 被唤醒, 返回打断状态为 true interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

可打断模式:

    private void doAcquireInterruptibly(int arg)throws InterruptedException {final Node node = addWaiter(Node.EXCLUSIVE);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())// 在 park 过程中如果被 interrupt  这时候抛出异常, 而不会再次进入 for (;;)throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}

公平锁实现:

 // 与非公平锁主要区别在于 tryAcquire 方法的实现
protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// 先检查 AQS 队列中是否有前驱节点, 没有才去竞争 if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}}public final boolean hasQueuedPredecessors() {// The correctness of this depends on head being initialized// before tail and on head.next being accurate if the current// thread is first in queue.Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;// h != t 时表示队列中有 Node return h != t &&// (s = h.next) == null 表示队列中还有没有老二 ((s = h.next) == null ||// 或者队列中老二线程不是此线程s.thread != Thread.currentThread());}

条件变量实现(await)

public final void await() throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();//在condition队列中添加该线程的节点Node node = addConditionWaiter();//释放所有锁(锁重入也会一起释放完)int savedState = fullyRelease(node);int interruptMode = 0;while (!isOnSyncQueue(node)) {//停下LockSupport.park(this);if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}if (acquireQueued(node, savedState) && interruptMode != THROW_IE)interruptMode = REINTERRUPT;if (node.nextWaiter != null) // clean up if cancelledunlinkCancelledWaiters();if (interruptMode != 0)reportInterruptAfterWait(interruptMode);}private Node addConditionWaiter() {Node t = lastWaiter;// If lastWaiter is cancelled, clean out.if (t != null && t.waitStatus != Node.CONDITION) {unlinkCancelledWaiters();t = lastWaiter;}// 创建一个关联当前线程的新 Node, 添加至队列尾部Node node = new Node(Thread.currentThread(), Node.CONDITION);if (t == null)firstWaiter = node;elset.nextWaiter = node;lastWaiter = node;return node;}

条件变量(sigal)

  public final void signal() {//只有当前运行的线程才能调用signalif (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;if (first != null)doSignal(first);}private void doSignal(Node first) {do {// 已经是尾节点了if ( (firstWaiter = first.nextWaiter) == null)lastWaiter = null;first.nextWaiter = null;} while (// 将等待队列中的 Node 转移至 AQS 队列, 不成功且还有节点则继续循环!transferForSignal(first) &&// 队列还有节点 (first = firstWaiter) != null);}final boolean transferForSignal(Node node) {/** If cannot change waitStatus, the node has been cancelled.*/// 如果状态已经不是 Node.CONDITION(-2), 说明被取消了if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))return false;// 加入 AQS 队列尾部 Node p = enq(node);int ws = p.waitStatus;// 上一个节点被取消 if (ws > 0 ||
// // 上一个节点不能设置状态为 Node.SIGNAL!compareAndSetWaitStatus(p, ws, Node.SIGNAL))// unpark 取消阻塞, 让线程重新同步状态 LockSupport.unpark(node.thread);return true;}

ReentrantLock 原理(源码轰炸)相关推荐

  1. k线顶分型 python_顶底分型-(K线分类及顶底分型的一种数学原理 源码 贴图)...

    好股票软件下载网(www.goodgupiao.com)提示:您正在下载的是:顶底分型-(K线分类及顶底分型的一种数学原理 源码 贴图) 参考缠论,研究了很多天终于将顶底分型进行了具体的数学量化,涵盖 ...

  2. Unity Fog 原理 源码分析 案例

    Unity Fog 原理 源码分析 案例 效果图 简述 背景知识 clip空间坐标的范围 d3d (near,0), unity remapping to (0,far) 越靠近相机z值越小 open ...

  3. Delaunay 三角剖分3D(原理 + 源码)

    文章目录 Delaunay 三角剖分 二维与三维的区别 代码实现< Python版本 > 代码实现< Matlab 版本 > Delaunay 三角剖分 原理部分请参照我的上一 ...

  4. 仿猎豹垃圾清理 实现原理+源码

    仿猎豹垃圾清理(实现原理+源码) 转载请注明出处: 仿猎豹垃圾清理(实现原理+源码) 前几天无意打开猎豹内存大师, 发现它的垃圾清理很强大, 效果也不错, 闲着就研究了下. 不过.. 结果貌似和我想象 ...

  5. 仿猎豹垃圾清理(实现原理+源码)

    仿猎豹垃圾清理(实现原理+源码) 转载请注明出处: 仿猎豹垃圾清理(实现原理+源码) 前几天无意打开猎豹内存大师, 发现它的垃圾清理很强大, 效果也不错, 闲着就研究了下. 不过.. 结果貌似和我想象 ...

  6. 仿iOS猎豹垃圾清理(实现原理+源码)

    转载请注明出处: 仿猎豹垃圾清理(实现原理+源码) 前几天无意打开猎豹内存大师, 发现它的垃圾清理很强大, 效果也不错, 闲着就研究了下. 不过.. 结果貌似和我想象的不太一样.怎么说呢, 听我下文一 ...

  7. c++ socket线程池原理_ThreadPoolExecutor线程池实现原理+源码解析

    推荐学习 被微服务轰炸?莫怕!耗时35天整出的「微服务学习教程」送你 死磕「并发编程」100天,全靠阿里大牛的这份最全「高并发套餐」 闭关28天,奉上[Java一线大厂高岗面试题解析合集],备战金九银 ...

  8. SpringBoot(2.4.0)自动配置原理(源码)

    一.从@SpringBootApplication讲起 源码 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Docu ...

  9. Mybatis Interceptor 拦截器原理 源码分析

    Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最 ...

最新文章

  1. 机器学习项目模板:ML项目的6个基本步骤
  2. 你需要的不是重构,而是理清业务逻辑(转)
  3. [九省联考2018]IIIDX
  4. mysql 5.7 学习
  5. [剑指offer]面试题5:从尾到头打印链表
  6. 2021年中国物流地产行业发展报告
  7. JavaScript 22岁生日快乐!
  8. java web远程调试工具_java远程调试 - Dendy的个人页面 - OSCHINA - 中文开源技术交流社区...
  9. 拓端tecdat|R语言CRAN软件包Meta分析
  10. java servlet api.jar_servlet-api.jar包下载
  11. 高仿TIMI页面易语言源码-已对接易游网络验证
  12. 计算机毕业设计之 疫情防控志愿者管理系统
  13. 《设计模式修炼真经》15 — 模板方法模式
  14. 小马过河(计算几何)
  15. 声纹采集是什么?标准声纹采集终端设备的作用是什么?
  16. 一个微型的操作系统内核 MiniOS
  17. 苹果手机app应用显示未受信任打不开怎么办
  18. 男式西服的三大色系介绍
  19. ooXMLAgile Encryption(一)文档结构
  20. 杭州机械工程师职称评审条件

热门文章

  1. com.google.zxing 二维码工具类
  2. 安卓自定义音量键_iPhone越狱丨利用安卓手机为你的iPhone越狱!
  3. 读《微波工程(第三版)》笔记 (4:波方程和基本平面波的解)
  4. 【项目相关技术】前端由来、vscode安装使用、ECMAScript 6、Vue、element-ui、Node.js、npm、模块化、WebPack
  5. Spring Cloud架构教程 (三)服务网关(基础)
  6. 坐标下降法(Coordinate descent)
  7. day05 【异常、线程】
  8. 2022年七夕送女朋友什么礼物好?实用且好看的礼物推荐
  9. 国内平台游戏借苹果iOS爆发:游戏开发产值过亿
  10. 2.2.8 SEA Shared Ethernet Adapter共享以太网卡