阿粉写了八千多字,就是为了把 ReentrantLock 讲透(三)

简介: 啥是可重入锁呢?比如:线程 1 通过调用 lock() 方法获取锁之后,再调用 lock 时,就不会再进行阻塞获取锁,而是直接增加重试次数。 还记得 synchronized 吗?它有 monitorenter 和 monitorexit 两种指令来保证锁,而它们的作用可以理解为每个锁对象拥有一个锁计数器,也就是如果再次调用 lock() 方法,计数器会进行加 1 操作

AQS.unparkSuccessor

释放锁成功之后,接下来要做的就是唤醒后面的进程,这个方法是在 AQS 中实现的

主要逻辑是:

  • 获取当前节点状态,如果小于 0 ,则置为 0
  • 获取当前节点的下一个节点,如果不为空,直接唤醒
  • 如果为空,或者节点状态大于 0 ,则寻找下一个状态小于 0 的节点

代码的具体实现

private void unparkSuccessor(Node node) {
 // 获取当前节点的状态
    int ws = node.waitStatus;
    // 如果节点状态小于 0 ,则进行 CAS 操作设置为 0
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    // 获取当前节点的下一个节点 s
    Node s = node.next;
    // 如果 s 为空,则从尾部节点开始,或者s.waitStatus 大于 0 ,说明节点被取消
    // 从尾节点开始,寻找到距离 head 节点最近的一个 waitStatus <= 0 的节点
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
     // next 节点不为空,直接唤醒即可
        LockSupport.unpark(s.thread);
}

为什么要从尾节点开始寻找距离 head 节点最近的一个 waitStatus <= 0 的节点呢?

这是因为在 enq() 构建节点的方法中,最后是 t.next = node (忘了就再往上翻翻看),设置原来的 tail 的 next 节点指向新的节点

如果在 CAS 操作之后, t.next = node 操作之前,有其他线程调用 unlock 方法从 head 开始向后遍历,因为此时 t.next = node 还没有执行结束,意味着链表的关系还没有建立好,这样就会导致遍历的时候到 t 节点这里发生中断,因为此时 tail 还没有指向新的尾节点

如果从后向前遍历的话,就不会存在这样的问题

接下来下一个线程就被唤醒了,然后程序会把它当成新的节点开始执行

而原来执行结束的线程,则会将它从队列中移除,然后开始循环循环

这篇文章终于讲完了,阿粉的头发都快秃了

19.jpg

相关文章
|
运维 Linux 程序员
最全查看Linux系统状态脚本_linux查询所有服务器信息的脚本,墙都不扶就服你
最全查看Linux系统状态脚本_linux查询所有服务器信息的脚本,墙都不扶就服你
最全查看Linux系统状态脚本_linux查询所有服务器信息的脚本,墙都不扶就服你
|
机器学习/深度学习 TensorFlow API
机器学习实战:TensorFlow在图像识别中的应用探索
【10月更文挑战第28天】随着深度学习技术的发展,图像识别取得了显著进步。TensorFlow作为Google开源的机器学习框架,凭借其强大的功能和灵活的API,在图像识别任务中广泛应用。本文通过实战案例,探讨TensorFlow在图像识别中的优势与挑战,展示如何使用TensorFlow构建和训练卷积神经网络(CNN),并评估模型的性能。尽管面临学习曲线和资源消耗等挑战,TensorFlow仍展现出广阔的应用前景。
342 5
ARM64技术 —— MMU处于关闭状态时,内存访问是怎样的?
ARM64技术 —— MMU处于关闭状态时,内存访问是怎样的?
|
Kubernetes 负载均衡 网络协议
在k8S中,Servic类型有哪些?
在k8S中,Servic类型有哪些?
|
安全 算法 Java
强大!基于Spring Boot 3.3 六种策略识别上传文件类型
【10月更文挑战第1天】在Web开发中,文件上传是一个常见的功能需求。然而,如何确保上传的文件类型符合预期,防止恶意文件入侵,是开发者必须面对的挑战。本文将围绕“基于Spring Boot 3.3 六种策略识别上传文件类型”这一主题,分享一些工作学习中的技术干货,帮助大家提升文件上传的安全性和效率。
664 0
|
并行计算 Ubuntu
ubuntu彻底卸载Nvidia显卡驱动
ubuntu彻底卸载Nvidia显卡驱动
2427 4
|
安全 测试技术 持续交付
在Python Web开发中,测试是一个至关重要的环节
【5月更文挑战第12天】在Python Web开发中,测试至关重要,包括单元测试(unittest模块)、集成测试、功能测试、系统测试、验收测试、性能测试、安全测试和端到端测试。常用的测试工具有unittest、pytest、selenium、requests和coverage。遵循“测试先行”和“持续集成”原则,确保代码质量与稳定性。
265 3
|
算法 调度 数据库
【C++20 新特性 Calendar (C++20) − Time zone (C++20)】时间的艺术与科学: C++中的现代日期和时区处理
【C++20 新特性 Calendar (C++20) − Time zone (C++20)】时间的艺术与科学: C++中的现代日期和时区处理
657 3
|
存储 缓存 负载均衡
上亿用户级别的短视频APP服务器需要多少钱?
上亿用户级别的短视频APP运营成本高昂,仅CDN一年就可能需2000万。这类应用需复杂的分布式系统,包括云服务器、负载均衡、云数据库、数据库缓存、云存储和CDN等。考虑到自建IDC中心的巨额初期投入及运维成本,租用云服务器成为初创公司的优选,如阿里云提供的成熟解决方案和针对初创企业的补贴计划。
333 0
|
存储 安全 Java
CopyOnWriteArrayList底层原理全面解析【建议收藏】
CopyOnWriteArrayList是Java中的一个线程安全的集合类,是ArrayList线程安全版本,主要通过Copy-On-Write(写时复制,简称COW)机制来保证线程安全。 Copy-On-Write机制核心思想:向一个数组中添加数据时,不直接操作原始数组,而是拷贝原始数组生成一份原始数组副本,将需要添加的数据添加到原始数组副本中,操作完成后再用原始数组副本直接替换原始数组,从而保证多个线程同时操作原始数组时的线程安全。