🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐➕关注👀是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝+关注👀欢迎留言讨论

🔥🔥🔥(源码获取 + 调试运行 + 问题答疑)🔥🔥🔥  有兴趣可以联系我

🔥🔥🔥  文末有往期免费源码,直接领取获取(无删减,无套路)

我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。

前言

在Java并发编程中,AbstractQueuedSynchronizer(AQS)是整个JUC并发包的基石。而理解AQS的关键,就在于深入掌握其内部类Node的设计原理。Node不仅是构成AQS同步队列和条件队列的基本单元,更是整个同步机制状态流转的核心载体。本文将带大家深入剖析Node类的源码结构,详细解析每个字段的含义,并重点探讨waitStatus状态机的工作原理。

Node类的整体结构

Node类是AQS的内部类,位于java.util.concurrent.locks.AbstractQueuedSynchronizer中。它作为AQS队列的基本节点,承担着线程封装、状态管理和队列链接的重要职责。

 static final class Node {
     // 节点模式常量
     static final Node SHARED = new Node();
     static final int CANCELLED =  1;
     static final int SIGNAL    = -1;
     static final int CONDITION = -2;
     static final int PROPAGATE = -3;
 ​
     // 节点字段
     volatile int waitStatus;
     volatile Node prev;
     volatile Node next;
     volatile Thread thread;
     Node nextWaiter;
     
     // 构造方法和其他方法...
 }

核心字段深度解析

1. 线程引用(thread)

volatile Thread thread字段保存了被封装在节点中的线程引用。当线程尝试获取锁失败时,AQS会将该线程包装成Node节点加入到同步队列中。

设计意义

  • 实现了线程与队列节点的绑定

  • 为后续的线程唤醒提供目标对象

  • volatile修饰确保了线程间的可见性

2. 等待状态(waitStatus)

volatile int waitStatus是Node类中最重要的字段,它是一个状态机,控制着节点的生命周期和行为模式。waitStatus包含以下几种状态:

CANCELLED (1)

表示节点已被取消。当线程等待超时或被中断时,节点会进入此状态。一旦进入CANCELLED状态,节点将不再参与同步竞争。

状态迁移场景

  • 等待超时(如tryAcquireNanos

  • 线程被中断且响应中断

  • 获取锁过程中发生异常

SIGNAL (-1)

这是同步队列中最常见的状态。表示当前节点的后继节点需要被唤醒。当一个节点的waitStatus为SIGNAL时,它在释放锁时有责任唤醒其后继节点。

工作机理

 // 在shouldParkAfterFailedAcquire方法中的典型应用
 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
     int ws = pred.waitStatus;
     if (ws == Node.SIGNAL)
         return true; // 前驱节点为SIGNAL,当前线程可以安全阻塞
     if (ws > 0) {
         // 跳过已取消的前驱节点
         do {
             node.prev = pred = pred.prev;
         } while (pred.waitStatus > 0);
         pred.next = node;
     } else {
         // 将前驱节点状态设置为SIGNAL
         compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
     }
     return false;
 }
CONDITION (-2)

表示节点处于条件队列中。该状态与ConditionObject配合使用,用于实现条件变量功能。

特点

  • 节点从同步队列转移到条件队列时状态变为CONDITION

  • 处于CONDITION状态的节点不会参与同步竞争

  • 当条件满足时,节点从条件队列转移回同步队列,状态重置为0

PROPAGATE (-3)

仅在共享模式下使用,表示后续的acquireShared操作应该无条件传播。这个状态主要用于解决共享锁传播的竞争条件。

典型应用场景: 在doReleaseShared方法中,当头节点状态从SIGNAL变为0后,如果检测到有后续的共享节点,会将状态设置为PROPAGATE以确保传播继续。

状态0

初始状态,表示节点刚被创建还未进入任何特定状态。

3. 前驱与后继指针(prev & next)

volatile Node prevvolatile Node next构成了AQS同步队列的双向链表结构。

设计考量

  • 双向链表:便于节点的删除操作,特别是在处理CANCELLED节点时

  • volatile修饰:确保多线程环境下的内存可见性

  • CAS操作:所有指针修改都通过CAS保证原子性

4. 下一个等待条件(nextWaiter)

Node nextWaiter字段有两种用途:

  1. 在条件队列中,指向条件队列中的下一个等待节点

  2. 在同步队列中,用于标识节点模式(SHARED或EXCLUSIVE)

5. 节点模式(SHARED/EXCLUSIVE)

通过isShared()方法判断节点模式:

 final boolean isShared() {
     return nextWaiter == SHARED;
 }

两种模式对比

模式 标识 应用场景 唤醒策略
独占模式 EXCLUSIVE ReentrantLock等 只唤醒一个后继节点
共享模式 SHARED Semaphore、CountDownLatch等 传播式唤醒多个节点

waitStatus状态机的工作原理

状态流转图

waitStatus的状态流转是一个典型的状态机模式,理解这个状态机是掌握AQS的关键。

SIGNAL状态的核心作用

SIGNAL状态的设计体现了AQS的"责任链"思想。每个节点都对其后继节点负有通知责任:

  1. 设置SIGNAL:在获取锁失败后,线程会将前驱节点的waitStatus设置为SIGNAL

  2. 检查SIGNAL:线程在阻塞前会检查前驱节点是否为SIGNAL状态

  3. 履行通知义务:节点释放锁时,如果检测到SIGNAL状态,会唤醒后继节点

这种设计确保了同步队列的可靠性和高效性。

PROPAGATE状态的精妙之处

PROPAGATE状态解决了共享模式下的一种极端竞争条件。考虑以下场景:

  1. 线程A释放共享锁,将头节点状态从SIGNAL改为0

  2. 线程B此时获取共享锁成功,但在设置新头节点前被挂起

  3. 线程C也来释放锁,看到头节点状态为0,认为不需要传播

  4. 如果没有PROPAGATE状态,等待的线程可能无法被及时唤醒

PROPAGATE状态的确立确保了共享锁传播的连续性。

实际应用中的注意事项

1. 节点取消的优化处理

在节点取消时,AQS会执行清理操作,跳过所有CANCELLED状态的节点:

 private void cancelAcquire(Node node) {
     if (node == null)
         return;
     
     node.thread = null;
     Node pred = node.prev;
     while (pred.waitStatus > 0)
         node.prev = pred = pred.prev;
     
     // 详细的清理逻辑...
 }

2. 条件队列与同步队列的转换

当调用Condition.signal()时,节点会从条件队列转移到同步队列,同时状态从CONDITION变为0:

 final boolean transferForSignal(Node node) {
     if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
         return false;
     
     Node p = enq(node);
     int ws = p.waitStatus;
     if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
         LockSupport.unpark(node.thread);
     return true;
 }

总结

Node作为AQS的核心内部类,通过精巧的状态设计和指针管理,构建了一套高效、可靠的同步机制。waitStatus状态机不仅是节点状态的真实反映,更是整个AQS同步逻辑的指挥棒。深入理解Node的工作原理,对于掌握Java并发编程、诊断并发问题、甚至设计自己的同步器都具有重要意义。

通过对Node源码的剖析,我们不仅看到了Doug Lea大师精湛的设计功力,更能体会到优秀并发设计的核心思想:通过状态机的精确控制和责任链的明确划分,在保证正确性的前提下最大化性能。


「在线考试系统源码(含搭建教程)」 (无删减,无套路):🔥🔥🔥  

链接:https://pan.quark.cn/s/96c4f00fdb43 提取码:WR6M


往期免费源码对应视频:

免费获取--SpringBoot+Vue宠物商城网站系统

🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐➕关注👀是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝+关注👀欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥  有兴趣可以联系我

💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!

💖常来我家多看看,
📕网址:扣棣编程
🎉感谢支持常陪伴,
🔥点赞关注别忘记!

💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!

⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇点击此处获取源码⬇⬇⬇⬇⬇⬇⬇⬇⬇

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐