java中的锁之AbstractQueuedSynchronizer源码分析(一)

时间:2022-11-12 21:22:30

一、AbstractQueuedSynchronizer类介绍。

该抽象类有两个内部类,分别是静态不可继承的Node类和公有的ConditionObject类。AbstractQueuedSynchronizer的核心实现是一个双向队列,队列中的每一个元素是一个Node。Node是AbstractQueuedSynchronizer定义的一个内部类。每个Node有一个前向指针prev和一个后向指针、和这个Node关联的thread以及一个状态位。而AbstractQueuedSynchronizer 则维护了一个head节点和一个tail节点。AbstractQueuedSynchronizer的实现的队列是一种FIFO的队列,它保证位于队列前面的Node(其关联的线程),将总能第一个尝试去争用需要同步的资源。前面的Node(其关联的线程)在释放资源时,会去唤醒后继节点。

队列结构虽然简单,但是在涉及需要同步的场景中,会有多个线程同时使用这一个队列。每个线程均会尝试访问和修改队列的head和tail,并修改与自己关联的Node的prev和next指针,而每个Node又有可能被其他线程访问。所以,可以看到在上述代码的成员变量的声明中,均使用了volatile关键字,使得所有对变量的改动都直接刷新到内存中,而不采取寄存器缓存。

AQS是通过一个双向的FIFO 同步队列来完成同步状态的管理,当有线程获取锁失败后,就被添加到队列末尾,让我看一下这个队列

java中的锁之AbstractQueuedSynchronizer源码分析(一)

红色节点为头结点,可以把它当做正在持有锁的节点,

下面解释下waitStatus五个的得含义:

  • CANCELLED(1):该节点的线程可能由于超时或被中断而处于被取消(作废)状态,一旦处于这个状态,节点状态将一直处于CANCELLED(作废),因此应该从队列中移除.
  • SIGNAL(-1):当前节点为SIGNAL时,后继节点会被挂起,因此在当前节点释放锁或被取消之后必须被唤醒(unparking)其后继结点.
  • CONDITION(-2) 该节点的线程处于等待条件状态,不会被当作是同步队列上的节点,直到被唤醒(signal),设置其值为0,重新进入阻塞状态.
  • 0:新加入的节点

在锁的获取时,并不一定只有一个线程才能持有这个锁(或者称为同步状态),所以此时有了独占模式和共享模式的区别,也就是在Node节点中由nextWait来标识。比如ReentrantLock就是一个独占锁,只能有一个线程获得锁,而WriteAndReadLock的读锁则能由多个线程同时获取,但它的写锁则只能由一个线程持有。这次先介绍独占模式下锁(或者称为同步状态)的获取与释放.这个类使用到了模板方法设计模式:定义一个操作中算法的骨架,而将一些步骤的实现延迟到子类中。

二、Node类介绍。

1、简介。一个Node对象代表一个线程,线程状态保存在Node类里面的waitStatus字段里面。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

2、源码。用于存放获取线程的节点。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

########################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

################################################################################################

3、说明。

(1)SHARED成员变量。该成员变量是本类对象的特殊情况。如果在类中初始化该成员变量为非null时,那么本类对象的成员变量一定是static类型,即必须是类变量,这样在内存中只分配一次,这样每个该类的对象共享同一份该类特殊的一份成员。如果仅仅是声明,或者是初始化为null,则无任何要求,此时仅仅是分配一个引用(指针)所占用的内存空间,而不用构造方法来递归分配空间。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

(2)isShared方法。该方法就是判断Node类型的nextWaiter成员变量是否就是所有Node类型的对象都共享的那一份  new Node()对象。只要该类被加载了,那么就存在唯一的一份共享new Node()对象。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

(3)predecessor方法。该方法就是判断该Node节点对象是否有前驱节点(即判断该Node对象的pre成员变量是否为空),如果有就返回前驱节点,没有则抛出空指针异常。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

(4)该类一共有5个本类对象的成员变量。

a、一个共享的成员变量SHARED,标识节点当前在共享模式下。

b、一个Node类型的指向NULL的共享变量EXCLUSIVE,标识节点当前在独占模式下。

c、一个前驱节点成员变量pre,前驱节点的引用。prev 和 next 用于实现阻塞队列的双向链表。首先, prev节点在队列里面, 则 prev != null 肯定成立;其次,prev != null 成立, 不一定 node 就在 Sync Queue 里面。

d、一个后继节点的成员变量next,后继节点的引用。prev 和 next 用于实现阻塞队列的双向链表。

e、一个等待节点的成员变量nextWaiter。注意这里的等待节点的成员变量nextWaiter没有被volatile修饰。nextWaiter 用于实现条件队列的单向链表。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

(5)Thread类型的成员变量thread。这个就是线程本尊。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

(6)节点状态的成员变量waitStatus。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

(7)常量介绍。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

a、CANCELLED,说明节点已经取消获取 lock 了(一般是由于 interrupt 或 timeout 导致的) 很多时候是在 cancelAcquire 里面进行设置这个标识。

b、SIGNAL, 标识当前节点的后继节点需要唤醒(PS: 这个通常是在 独占模式下使用, 在共享模式下有时用 PROPAGATE)。

c、CONDITION,表示当前节点在 Condition Queue 里面。

d、PROPAGATE,当前节点获取到 lock 或进行 release lock 时, 共享模式的最终状态是 PROPAGATE(PS: 有可能共享模式的节点变成 PROPAGATE 之前就被其后继节点抢占 head 节点, 而从Sync Queue中被踢出掉)。

(8)初始化Node,用于同步队列。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

(9)初始化Node,用于条件队列。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

三、ConditionObject类介绍。

1、简介。

ConditionObject是AbstractQueuedSynchronizer的内部类,它为锁机制维护了另一个队列,如果线程排在了该队列中,说明这个线程需要在某种条件满足后,才被唤醒。从这里可以看到,ConditionObject是作为AbstractQueuedSynchronizer的内部类来实现的,这表示,得首先有一个AbstractQueuedSynchronizer的实例,才能新建一个ConditionObject。这更进一步说明了,Condition存在的前提是必须有Lock

   可以看到,synchronized和Lock在功能上类似,都可以保证多线程对一段代码的顺序执行;Object monitor methods则和Condition类似,前者在某个object上进行等待和唤醒,而后者在某个条件上进行等待和唤醒java其实是先有的synchronized&Object monitor methods,后有的Lock&Condition。

ConditionObject对象。一个该对象,包含有Node类型的两个成员变量firstWaiter和lastWaiter,分别指向单向Node队列的头部和尾部。而Node队列是由Node类中Node类型的成员变量nextWaiter来串联起来,也就是一个Node类型的单向链表数据结构。如下图。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

2、源码。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

###########################################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

×××××××××××××××××××××××××××××××××××××××××

这里调用了AQS里面的成员方法。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

×××××××××××××××××××××××××××××××××××××××××

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

##############################################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

##########################################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

#########################################################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

#########################################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

#################################################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

################################################################################

查询这个condition对象上,是否含有在等待的线程。通过遍历单链表,查看Node节点的成员变量waitStatus是否等于Node节点中的CONDITION的值,该值表示该Node节点中代表的线程是在条件队列中。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

###############################################################

获取等待队列的大约长度。因为在查询的时候并没有加锁,有可能在查询的时候有新的节点加入单向队列中。通过遍历,查看Node节点的成员变量waitStatus是否等于Node节点中的CONDITION的值,然后计数统计。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

################################################

获取等待在condition对象上的线程集合。通过遍历单向链表,通过Node类的waitStatus成员变量的值标识该Node节点是否在该条件队列上。inter

java中的锁之AbstractQueuedSynchronizer源码分析(一)

############################################

注意:

因为成员内部类的创建依赖于外部类对象。表达的是一个成员内部类对象有且仅有一个外部类对象一一对应。那么可以判断一个外部类对象是不是该成员内部类对象的创建者。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

############################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

############################################

当Node在Condition Queue 中, 若状态不是 CONDITION, 则一定是 被中断或超时。在Condition Queue 里面 Node.waitStatus 只有可能是 CONDITION 或是 0(timeout/interrupt引起的)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

################################

java中的锁之AbstractQueuedSynchronizer源码分析(一)

java中的锁之AbstractQueuedSynchronizer源码分析(一)

3、说明。

(1)public内部类。单向队列。

java中的锁之AbstractQueuedSynchronizer源码分析(一)

(2)

4、

5、

四、

java中的锁之AbstractQueuedSynchronizer源码分析(一)的更多相关文章

  1. java中的锁之AbstractQueuedSynchronizer源码分析(二)

    一.成员变量. 1.目录. 2.state.该变量标记为volatile,说明该变量是对所有线程可见的.作用在于每个线程改变该值,都会马上让其他线程可见,在CAS(可见锁概念与锁优化)的时候是必不可少 ...

  2. Java并发编程笔记之AbstractQueuedSynchronizer源码分析

    为什么要说AbstractQueuedSynchronizer呢? 因为AbstractQueuedSynchronizer是JUC并发包中锁的底层支持,AbstractQueuedSynchroni ...

  3. Java锁及AbstractQueuedSynchronizer源码分析

    一,Lock 二,关于锁的几个概念 三,ReentrantLock类图 四,几个重要的类 五,公平锁获取 5.1 lock 5.2 acquire 5.3 tryAcquire 5.3.1 hasQu ...

  4. Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

    在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概 ...

  5. Java并发系列&lbrack;3&rsqb;----AbstractQueuedSynchronizer源码分析之共享模式

    通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取.在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快 ...

  6. Java并发系列&lbrack;4&rsqb;----AbstractQueuedSynchronizer源码分析之条件队列

    通过前面三篇的分析,我们深入了解了AbstractQueuedSynchronizer的内部结构和一些设计理念,知道了AbstractQueuedSynchronizer内部维护了一个同步状态和两个排 ...

  7. AbstractQueuedSynchronizer源码分析

    AbstractQueuedSynchronizer源码分析 前提 AQS(java.util.concurrent.locks.AbstractQueuedSynchronizer)是并发编程大师D ...

  8. RocketMQ中Broker的HA策略源码分析

    Broker的HA策略分为两部分①同步元数据②同步消息数据 同步元数据 在Slave启动时,会启动一个定时任务用来从master同步元数据 if (role == BrokerRole.SLAVE) ...

  9. Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析

    经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...

随机推荐

  1. phpMyAdmin下载与安装

    Part1 phpMyAdmin下载 浏览器输入网址 http://www.phpmyadmin.net 下载即可 我的下载是这样的 Part2 phpMyAdmin安装 解压下载的压缩包到apach ...

  2. Tab标签栏 切换 权威总结

    angular的标签栏,有两种方法实现: 内容全部加载到页面中,再利用ng-show指令. 将每一块要加载的内容做成模板,利用ng-if指令加载. 用bootstrap的tab组件 用angular的 ...

  3. vb6&period;0 倒计时

    Dim t Dim start As Boolean Private Sub Command1_Click() If start = False Then t = Val(Text1) * 3600 ...

  4. Hibernate(二)之Hibernate-api详解

    一.Hibernate体系结构 二.Hibernate-api详解 2.1.Configuration配置对象 Configuration是用来加载配置文件的 我们Hibernate中主要有两个配置文 ...

  5. JDK、Eclipse、Myeclipse、Tomcat等各种软件的版本详解(写给对版本和兼容性问题焦头烂额的你)

    这篇文章我们来探讨一下关于JDK.Eclipse.Myeclipse.Tomcat的版本问题.一.关于版本的几个概念1.32位和64位两个版本:    简言之,64位的操作系统支持识别4G以上的内存条 ...

  6. UNIX网络编程——解决TCP网络传输&OpenCurlyDoubleQuote;粘包”问题

    当前在网络传输应用中,广泛采用的是TCP/IP通信协议及其标准的socket应用开发编程接口(API).TCP/IP传输层有两个并列的协议:TCP和UDP.其中TCP(transport contro ...

  7. SSIS Hekaton In-Memory OLTP 【翻译一篇外国文章】

    来自:http://www.itprotoday.com/microsoft-sql-server/important-new-features-sql-server-2014 Microsoft's ...

  8. C博客01--顺序、分支结构

    1.本章学习总结 1.1 思维导图 1.2 本章学习体会及代码量学习体会 1.2.1 学习体会 经过一周的初步学习,对C语言我有了一定的认识,也体验到了代码的乐趣,这应该为我以后的学习开了一个好头.在 ...

  9. JVM、Gc工作机制详解

    JVM主要包括四个部分: 类加载器(ClassLoad) 执行引擎 内存区: 本地方法接口:类似于jni调本地native方法 内存区包括四个部分: 1.方法区:包含了静态变量.常量池.构造函数等 2 ...

  10. NLP related basic knowledge with deep learning methods

    NLP related basic knowledge with deep learning methods  2017-06-22   First things first >>> ...