浅谈Java并发 J.U.C之AQS:CLH同步队列

 更新时间:2019年05月21日 09:35:46   作者:chenssy  
AQS内部维护着一个FIFO队列,该队列就是CLH同步队列。下面小编来简单介绍下这个队列

CLH同步队列是一个FIFO双向队列,AQS依赖它来完成同步状态的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。

在CLH同步队列中,一个节点表示一个线程,它保存着线程的引用(thread)、状态(waitStatus)、前驱节点(prev)、后继节点(next),其定义如下:

static final class Node {
 /** 共享 */
 static final Node SHARED = new Node();

 /** 独占 */
 static final Node EXCLUSIVE = null;

 /**
 * 因为超时或者中断,节点会被设置为取消状态,被取消的节点时不会参与到竞争中的,他会一直保持取消状态不会转变为其他状态;
 */
 static final int CANCELLED = 1;

 /**
 * 后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点的线程得以运行
 */
 static final int SIGNAL = -1;

 /**
 * 节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()后,改节点将会从等待队列中转移到同步队列中,加入到同步状态的获取中
 */
 static final int CONDITION = -2;

 /**
 * 表示下一次共享式同步状态获取将会无条件地传播下去
 */
 static final int PROPAGATE = -3;

 /** 等待状态 */
 volatile int waitStatus;

 /** 前驱节点 */
 volatile Node prev;

 /** 后继节点 */
 volatile Node next;

 /** 获取同步状态的线程 */
 volatile Thread thread;

 Node nextWaiter;

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

 final Node predecessor() throws NullPointerException {
 Node p = prev;
 if (p == null)
 throw new NullPointerException();
 else
 return p;
 }

 Node() {
 }

 Node(Thread thread, Node mode) {
 this.nextWaiter = mode;
 this.thread = thread;
 }

 Node(Thread thread, int waitStatus) {
 this.waitStatus = waitStatus;
 this.thread = thread;
 }
}

CLH同步队列结构图如下:

入列

学了数据结构的我们,CLH队列入列是再简单不过了,无非就是tail指向新节点、新节点的prev指向当前最后的节点,当前最后一个节点的next指向当前节点。代码我们可以看看addWaiter(Node node)方法:

 private Node addWaiter(Node mode) {
 //新建Node
 Node node = new Node(Thread.currentThread(), mode);
 //快速尝试添加尾节点
 Node pred = tail;
 if (pred != null) {
 node.prev = pred;
 //CAS设置尾节点
 if (compareAndSetTail(pred, node)) {
 pred.next = node;
 return node;
 }
 }
 //多次尝试
 enq(node);
 return node;
 }

addWaiter(Node node)先通过快速尝试设置尾节点,如果失败,则调用enq(Node node)方法设置尾节点

 private Node enq(final Node node) {
 //多次尝试,直到成功为止
 for (;;) {
 Node t = tail;
 //tail不存在,设置为首节点
 if (t == null) {
 if (compareAndSetHead(new Node()))
 tail = head;
 } else {
 //设置为尾节点
 node.prev = t;
 if (compareAndSetTail(t, node)) {
 t.next = node;
 return t;
 }
 }
 }
 }

在上面代码中,两个方法都是通过一个CAS方法compareAndSetTail(Node expect, Node update)来设置尾节点,该方法可以确保节点是线程安全添加的。在enq(Node node)方法中,AQS通过“死循环”的方式来保证节点可以正确添加,只有成功添加后,当前线程才会从该方法返回,否则会一直执行下去。

过程图如下:

出列

CLH同步队列遵循FIFO,首节点的线程释放同步状态后,将会唤醒它的后继节点(next),而后继节点将会在获取同步状态成功时将自己设置为首节点,这个过程非常简单,head执行该节点并断开原首节点的next和当前节点的prev即可,注意在这个过程是不需要使用CAS来保证的,因为只有一个线程能够成功获取到同步状态。过程图如下:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家

相关文章

  • JavaWeb 使用DBUtils实现增删改查方式

    JavaWeb 使用DBUtils实现增删改查方式

    这篇文章主要介绍了JavaWeb 使用DBUtils实现增删改查方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • SpringDataRedis入门和序列化方式解决内存占用问题小结

    SpringDataRedis入门和序列化方式解决内存占用问题小结

    spring-data-redis是spring-data模块的一部分,专门用来支持在spring管理项目对redis的操作,这篇文章主要介绍了SpringDataRedis入门和序列化方式解决内存占用问题,需要的朋友可以参考下
    2022-12-12
  • SpringBoot使用邮箱发送验证码实现注册功能

    SpringBoot使用邮箱发送验证码实现注册功能

    这篇文章主要为大家详细介绍了SpringBoot使用邮箱发送验证码实现注册功能实例,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-02-02
  • 详解SpringBoot如何优雅的进行前后端通信

    详解SpringBoot如何优雅的进行前后端通信

    现在的项目基本上都是前后端分离的项目,如何打通前后端,接收前端传过来的参数呢,下面小编就来和大家详细介绍一下SpringBoot如何优雅的进行前后端通信
    2024-03-03
  • 使用Spring Boot+gRPC构建微服务并部署的案例详解

    使用Spring Boot+gRPC构建微服务并部署的案例详解

    这篇文章主要介绍了使用Spring Boot+gRPC构建微服务并部署,Spring Cloud仅仅是一个开发框架,没有实现微服务所必须的服务调度、资源分配等功能,这些需求要借助Kubernetes等平台来完成,本文给大家介绍的非常详细,需要的朋友参考下吧
    2022-06-06
  • 浅谈Java变量的初始化顺序详解

    浅谈Java变量的初始化顺序详解

    本篇文章是对Java变量的初始化顺序进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • Spring IOC容器基于XML外部属性文件的Bean管理

    Spring IOC容器基于XML外部属性文件的Bean管理

    这篇文章主要为大家介绍了Spring IOC容器Bean管理XML外部属性文件,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Feign远程调用丢失请求头问题

    Feign远程调用丢失请求头问题

    本文介绍了在服务端项目中如何解决资源访问限制问题,首先介绍了问题的产生,然后详细解析了源码,最后提出了解决方案,解决方案包括同步和异步两种,同步时直接向Spring容器注入RequestInterceptor拦截器
    2024-09-09
  • Java中的@Repeatable注解使用详解

    Java中的@Repeatable注解使用详解

    这篇文章主要介绍了Java中的@Repeatable注解使用详解,@Repeatable注解是java8为了解决同一个注解不能重复在同一类/方法/属性上使用的问题,本文提供了解决思路和部分实现代码,需要的朋友可以参考下
    2024-02-02
  • SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务(微服务实战)

    SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务(微服务实战)

    这篇文章主要介绍了SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务,系统可以支持多家云平台提供的短信服务。这里以阿里云和腾讯云为例,集成短信通知服务,需要的朋友可以参考下
    2021-11-11

最新评论