文档章节

Java并发编程:AbstractQueuedSynchronizer的内部结构

Java烂猪皮
 Java烂猪皮
发布于 2018/09/10 22:12
字数 2139
阅读 352
收藏 12

一 前言  

  虽然已经有很多前辈已经分析过AbstractQueuedSynchronizer(简称AQS,也叫队列同步器)类,但是感觉那些点始终是别人的,看一遍甚至几遍终不会印象深刻。所以还是记录下来印象更深刻,还能和大家一起探讨(这就是重复造轮子的好处,另外也主要是这篇篇幅太长了,犹豫了好久才决定写作)。既然有很多前辈都分析过这个类说明它是多么的重要,下面我们看下concurrent包的实现示意图就清楚AQS的所占有的地位了。

 

二 什么是AQS

  AbstractQueuedSynchronizer,中文简称队列同步器,英文简称AQS。它是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。从上面图可以看出AQS是实现锁或任意同步组件的关键,通过继承同步器并实现它的抽象方法来管理同步状态等。

在此我向大家推荐一个架构学习交流群。交流学习群号:478030634  里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

三 AQS的内部结构

  个人习惯喜欢先看其内部结构,因为内部结果是一个类实现的核心。经过分析得知:AQS类底层的数据结构是使用双向链表,包括head结点和tail结点,head结点主要用作后续的调度。另外还包含一个单向链表,只有当使用Condition时,才会存在此单向链表。并且可能会有多个Condition 链表(其中链表是队列的一种具体表现,所以也可称作队列)。如下图:

 

四 内部结构源码解析

3.1 类的继承关系

  

  1、说明它是一个抽象类,就说明它可能存在抽象方法需要子类去重写实现(具体有哪些方法需要重写后续会说明)。

  2、它还继承了AbstractOwnableSynchronizer(简称AOS)类可以设置独占资源线程和获取独占资源线程(独占锁会涉及到,AOS的源码自己可以进去看看)。

  另外建议各位多看看类上的注释,其实还蛮有作用的。

3.2 类的内部类

   先分析内部类中的结构再看AQS是怎么引用它的。下面先看Node.class,主要分析都在注释上了。

 

/**
 * Wait queue node class.
 * 注意看类上的注释,上面是原注释的第一行,表示等待队列节点类(虽然实际上是一个双向链表)。
 */
static final class Node {
    /**
     * 总共分为两者模式:共享和独占
     */
    /** 在共享模式中等待的节点 */
    static final Node SHARED = new Node();
    /** 在独占模式中等待的节点 */
    static final Node EXCLUSIVE = null;

    /**
     * 下面几个表示节点状态,也就是waitStatus所具有可能的值。
     */
    /**
     * 标记线程处于取消状态
     * 节点进入该状态就不会变化。
     * /
    static final int CANCELLED =  1;
    /**
     * 标记后继节点的线程处于等待状态,需要被取消停放(即被唤醒unpark)。
     * 变化情况:当当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点的线程得以运行。
     */
    static final int SIGNAL    = -1;
    /**
     * 标记线程正在等待条件(Condition),也就是该节点处于等待队列中。
     * 变化情况:当其他线程对Condition调用了signal()方法后,该节点将会从等待队列中转移到同步队列中,加入到同步状态的获取中。
     */
    static final int CONDITION = -2;
    /**
     * 表示下一次共享式同步状态获取将会无条件的被传播下去。
     */
    static final int PROPAGATE = -3;

    /**
     * 节点状态,包含上面四种状态(另外还有一种初始化状态0)
     * 特别注意:它是volatile关键字修饰的,保证对其线程可见性,但是不保证原子性。
     * 所以更新状态时,采用CAS方式去更新, 如:compareAndSetWaitStatus
     */
    volatile int waitStatus;

    /**
     * 前驱节点,比如当前节点被取消,那就需要前驱节点和后继节点来完成连接。
     */
    volatile Node prev;

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

    /**
     * 入队列时的当前线程。
     */
    volatile Thread thread;

    /**
     * 存储condition队列中的后继节点。
     */
    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节点,放入队列中。每个节点都包含了当前节点对应的线程、状态、前置节点引用、后继节点引用以及下一个等待者。

  其中还需要注意的是waitStatus对应的各个状态代表着什么意思,另外不清楚volatile关键字作用的请前去阅读下。

属性名称 描述
int waitStatus 表示节点的状态。其中包含的状态有:
  1. CANCELLED,值为1,表示当前的线程被取消;节点进入该状态就不会变化。
  2. SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;变化情况:当当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点的线程得以运行。
  3. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;变化情况:当其他线程对Condition调用了signal()方法后,该节点将会从等待队列中转移到同步队列中,加入到同步状态的获取中。
  4. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
  5. 值为0,表示当前节点在sync队列中,等待着获取锁。
Node prev
前驱节点,比如当前节点被取消,那就需要前驱节点和后继节点来完成连接。
Node next
后继节点。
Thread thread
入队列时的当前线程。
Node nextWaiter
存储condition队列中的后继节点。

  接下来简单看看ConditionObject的源码,后续我们会单独分析下这个类的作用。

 

/**
 * 实现Condition接口
 */
public class ConditionObject implements Condition, java.io.Serializable {
    private static final long serialVersionUID = 1173984872572414699L;
    /**
     * 条件队列的第一个节点。
     */
    private transient AbstractQueuedSynchronizer.Node firstWaiter;
    /**
     * 条件队列的最后一个节点。
     */
    private transient AbstractQueuedSynchronizer.Node lastWaiter;
}

 

  从中可以看它还是实现了Condition接口,而Condition接口又定义了什么规范呢?自己去看:),你会不会发现有点跟Object中的几个方法类似呢。

3.3 主要内部成员

 

// 头结点
    private transient volatile Node head;
    // 尾结点
    private transient volatile Node tail;
    // 同步状态
    private volatile int state;

 

 

五 总结

  通过上述分析就很清楚其内部结构是什么了吧。总结下:

  节点(Node)是成为sync队列和condition队列构建的基础,在同步器中就包含了sync队列(Node双向链表)。同步器拥有三个成员变量:sync队列的头结点head、sync队列的尾节点tail和状态state。对于锁的获取,请求形成节点,将其挂载在尾部,而锁资源的转移(释放再获取)是从头部开始向后进行。对于同步器维护的状态state,多个线程对其的获取将会产生一个链式的结构。

 

大家觉得文章对你还是有一点点帮助的,大家可以点击下方二维码进行关注。 《Java烂猪皮》 公众号聊的不仅仅是Java技术知识,还有面试等干货,后期还有大量架构干货。大家一起关注吧!关注烂猪皮,你会了解的更多..............

 

原文:https://www.cnblogs.com/yuanfy008/p/9608666.html

© 著作权归作者所有

共有 人打赏支持
Java烂猪皮
粉丝 51
博文 30
码字总数 82549
作品 0
黄浦
私信 提问
加载中

评论(1)

HWzhihu
HWzhihu
您好,华为目前发起了影响力孵化营,想寻找有撰写技术文章习惯的行业专家。合作方式:针对华为推荐或感兴趣的主题撰写技术类文章,投稿被采纳后,文章可发表到官方知乎和公众号等平台上,文末会为作者的个人自媒体账号引流。如果是非常优质的文章,我们还会联系推广资源,并获得物质激励、专家分享等奖励。欢迎您参加报名,有疑问咨询或报名参加可以添加微信hans_hss~
CountDownLatch源码解析

CountDownLatch 相比ReentranceLock,CountDownLatch的流程还是相对比较简单的,CountDownLatch也是基于AQS,它是AQS的共享功能的一个实现。 下面从源代码的实现上详解CountDownLatch。 1、C...

maxam0128
2018/01/23
0
0
Java并发同步器AQS(AbstractQueuedSynchronizer)学习笔记(1)

Java中的并发包,是在Java代码中并发程序的热门话题。如果我们去读concurrent包的源码时,会发现其真正的核心是 AbstractQueuedSynchronizer , 简称 AQS 框架 , 而 Doug Lea 大神正是此包的...

zavakid
2012/10/24
0
0
【Java并发专题】27篇文章详细总结Java并发基础知识

努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! github:https://github.com/CL0610/Java-concurrency,欢迎题issue和Pull request。所有的文档都是自己亲自码的,如果觉...

你听___
2018/05/06
0
0
Inside AbstractQueuedSynchronizer (4)

3.6 ConditionObject AbstractQueuedSynchronizer的内部类ConditionObject实现了Condition接口。Condition接口提供了跟Java语言内置的monitor机制类似的接口:await()/signal()/signalAll(),......

pczhangtl
2013/11/18
0
0
JAVA并发编程JUC基础学习(简介)

之前写过一篇并发编程的简单实例应用,Future快速实现并发编程,可以很快的在自己的项目中应用,但并不系统,之前说过总结一篇(或者一系列)java.util.concurrent 这个并发编程工具类的学习...

小海bug
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

MQ学习-基本概念区分

消息队列 Kafka 涉及的专有名词和术语进行定义和解释,方便您更好地理解相关概念并使用该产品。 Broker: 消息队列 Kafka 集群包含一个或多个消息处理服务器,该服务器被称为 Broker。 Topi...

os1cheng
13分钟前
0
0
腾讯怒怼:靠红包骗用户下载怎么可以叫产品

近日,社交圈出现了大动荡,三款新推出的社交软件全部被微信封杀,对此,腾讯公关总监在回应外界对于1月15日三款社交新产品撼动微信的消息,他呼吁媒体在批评的同时应当尊重事实,“我们尊重...

linux-tao
今天
1
0
面试必考-数据优化

sql语句优化 性能不理想的系统中除了一部分是因为应用程序的负载确实超过了服务器的实际处理能力外,更多的是因为系统存在大量的SQL语句需要优化。 为了获得稳定的执行性能,SQL语句越简单越好...

瑞查德-Jack
今天
2
0
spring aop学习

1.aop和spring aop的关系? aop是一种思想,spring aop是aop的一种实现。 2.Spring aop和AspectJ的关系? 这两种都是实现aop的,spring借助AspectJ的语法实现aop的功能 3.@AspectJ support和...

llsydn
今天
4
0
OSChina 周六乱弹 —— 世界的源代码

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @小鱼丁 :#今日歌曲推荐# 分享Jason Mraz的单曲《Prettiest Friend (Demo)》: 《Prettiest Friend (Demo)》- Jason Mraz 手机党少年们想听歌...

小小编辑
今天
475
11

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部