Java中的AQS同步队列问题详解

目录
  • AQS 同步队列
    • 1、AQS 介绍
      • 1.1、类图关系
      • 1.2、节点剖析
    • 2、AQS 实现原理
      • 2.1、队列初始化
      • 2.2、追加节点
    • 3、AQS 唤醒动作

AQS 同步队列

1、AQS 介绍

AQSAbstractQueuedSynchronizer 的缩写,他是一个抽象同步类,为 JUC 包下的大多数同步工具提供了核心实现,例如 ReentrantLock 的底层就是使用同步队列。AQS 提供一套基础的机制来实现线程的同步、阻塞与唤醒、等待队列等功能,也就是想要深入学习线程工具类,这个同步队列就必须得掌握。

1.1、类图关系

下面是整个 AQS 的类结构实现(从源码中直接打印的图,平常学习源码也可以这样打印出来观察整个程序的运行情况,有助于理解),从图中我们不难发现 AQS 内部持有两个 Node 类型的 headtail 属性,我们在什么时候会接触到头尾节点的定义,大家都是有经验的开发人员,肯定都能想到是链表当中。

在属性列中我们可以看见一个 state:int 这样的一个状态字段,Lock 的重入特性就是根据此来实现的,可以表示当前线程的锁重入次数。整个类继承自 AbstractOwnableSynchronizer 类,自然拥有对于其父类中属性的一些控制权,而里面的 Thraed 的线程就是表示当前持有锁的线程,在整个锁过程中具有很重要的地位。

1.2、节点剖析

当然链表只是一种组织存储形式的一种数据结构,这里叫做 FIFO 双向队列,至于为什么是双向的呢,看一下 Node 的节点定义就能明白,一个节点中含有 prevnext 节点来快速访问前驱和后继节点,不就是典型的双向形式呢。

相信大家在看到这个类字段的属性名定义之后就能才出来其的作用,但是这里还是介绍一下主要的几个字段含义,印证大家的猜想。

属性 作用
thread 表示当前节点封装的具体线程
SHARED 表示当前线程是获取共享资源时被阻塞
EXCLUSIVE 表示当前线程是获取独占资源时被挂起
prev 当前节点的前驱节点
next 当前节点的后继节点
waitStatus 记录当前线程的等待状态,其状态取值就是下面的四个字段
CANCELLED 取消线程
SIGNAL 线程需要被唤醒
CONDITION 线程在 condition 中等待
PROPAGATE 释放共享资源时需要通知其余节点线程

2、AQS 实现原理

上面我们知道了 AQS 其实就是一个双向的队列,如下图的结构一样。在线程获取锁失败的情况下,会被封装成一个 Node 节点而插入到队列当中;当其他的线程释放锁之后又会从队列中唤醒一个节点去争抢锁。

2.1、队列初始化

通过源码我们可以发现,在 AQS 进行初始化的时候的并没有对 headtail 进行初始化,而这两个节点是控制整个队列的,也就是说一开始整个队列处于 null 状态。

protected AbstractQueuedSynchronizer() {}

当第一个线程争抢锁失败之后会封装成 Node 进入到同步队列当中,这个时候就会进行判断,如果当前队列为空就会进行初始化(未进行初始化),初始化完成之后就将当前线程节点接在队列的尾部。

private final void initializeSyncQueue() {
    Node h;
    if (HEAD.compareAndSet(this, (Void)null, h = new Node())) {
        this.tail = h;
    }
}

2.2、追加节点

追加节点的操作就是简单的链表尾部添加节点的过程了,这里就不做过多的赘述。这里来看看前面初始化时会添加一个额外的节点在队列中,其实这个节点就是代表当前已经获取了锁的线程,至于为什么这么设置,大家往后看就明白了。

3、AQS 唤醒动作

在进行线程唤醒的过程中,会优先唤醒当前持有锁线程的下一个节点线程。

  • head 指针指向下一个节点;
  • 原来头结点的 next 指向 null;
  • 当前头结点的 prev 指向 null;
  • 当前头结点的 thread 指向 null。

这样就完成线程的唤醒操作了,但是这样来讲其实是不完美的,因为 AQS 只是一个抽象的统一工具,本身并没有对业务进行规范,还是要结合具体的实现类,例如 ReentrantLockCountDownLatchCyclicBarrier 这些的执行过程来进行分析。

到此这篇关于Java中的AQS同步队列的文章就介绍到这了,更多相关java AQS同步队列内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

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

    CLH同步队列是一个FIFO双向队列,AQS依赖它来完成同步状态的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态. 在CLH同步队列中,一个节点表示一个线程,它保存着线程的引用(thread).状态(waitStatus).前驱节点(prev).后继节点(next),其定义如下: static final class Node

  • Java并发编程之JUC并发核心AQS同步队列原理剖析

    目录 一.AQS介绍 二.AQS中的队列 1.同步等待队列 2.条件等待队列 3.AQS队列节点Node 三.同步队列源码分析 1.同步队列分析 2.同步队列--独占模式源码分析 3.同步队列--共享模式源码分析 一.AQS介绍 队列同步器AbstractQueuedSynchronizer(简称AQS),AQS定义了一套多线程访问共享资源的同步器框架,是用来构建锁或者其他同步组件的基础框架,是一个依赖状态(state)的同步器.Java并发编程的核心在java.util.concurrent(

  • Java中的AQS同步队列问题详解

    目录 AQS 同步队列 1.AQS 介绍 1.1.类图关系 1.2.节点剖析 2.AQS 实现原理 2.1.队列初始化 2.2.追加节点 3.AQS 唤醒动作 AQS 同步队列 1.AQS 介绍 AQS 是 AbstractQueuedSynchronizer 的缩写,他是一个抽象同步类,为 JUC 包下的大多数同步工具提供了核心实现,例如 ReentrantLock 的底层就是使用同步队列.AQS 提供一套基础的机制来实现线程的同步.阻塞与唤醒.等待队列等功能,也就是想要深入学习线程工具类,这

  • java中Executor,ExecutorService,ThreadPoolExecutor详解

    java中Executor,ExecutorService,ThreadPoolExecutor详解 1.Excutor 源码非常简单,只有一个execute(Runnable command)回调接口 public interface Executor { /** * Executes the given command at some time in the future. The command * may execute in a new thread, in a pooled thre

  • java 中maven pom.xml文件教程详解

    maven pom.xml文件教程详解,具体内容如下所示: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.x

  • 在Java中操作Zookeeper的示例代码详解

    依赖 <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.6.0</version> </dependency> 连接到zkServer //连接字符串,zkServer的ip.port,如果是集群逗号分隔 String connectStr = "192.

  • java中stringbuffer线程安全分析实例详解

    在对于一些类作用于线程时,安全系数高的线程更推荐大家使用,在尽可能的程度上降低程序出错的可能性.对于本篇所要提到的StringBuffer而言,在其缓冲区中有多个线程的存在,我们在查询其内部方法时发现了锁的存在.现在我们就StringBuffer线程.锁的应用.线程安全分析逐步带来介绍. 1.StringBuffer线程说明 Java.lang.StringBuffer线程安全的可变字符序列.一个类似于String的字符串缓冲区,但不能修改.虽然在任意时间点上它都包含某种特定的字符序列,但通过某

  • Java中内核线程理论及实例详解

    1.概念 内核线程是直接由操作系统内核控制的,内核通过调度器来完成内核线程的调度并负责将其映射到处理器上执行.内核态下的线程执行速度理论上是最高的,但是用户不会直接操作内核线程,而是通过内核线程的接口--轻量级进程来间接的使用内核线程.这种轻量级进程就是所谓的线程. 2.优点 由于内核线程的支持,每一个线程都是一个独立的单元,因此就算某一个线程挂掉了,也不会导致整个进程挂掉. 3.缺点 这种实现方式也存在局限性.由于是基于内核线程实现的,所以当涉及到线程的操作时(创建.运行.切换等)就涉及到系统

  • Java中Exception和Error的区别详解

    世界上存在永远不会出错的程序吗?也许这只会出现在程序员的梦中.随着编程语言和软件的诞生,异常情况就如影随形地纠缠着我们,只有正确的处理好意外情况,才能保证程序的可靠性. java语言在设计之初就提供了相对完善的异常处理机制,这也是java得以大行其道的原因之一,因为这种机制大大降低了编写和维护可靠程序的门槛.如今,异常处理机制已经成为现代编程语言的标配. 今天我要问你的问题是,请对比Exception和Error,另外,运行时异常与一般异常有什么区别? 典型回答 Exception和Error都

  • Java并发编程之阻塞队列深入详解

    目录 1. 什么是阻塞队列 2. 阻塞队列的代码使用 3. 生产者消费者模型 (1)应用一:解耦合 (2)应用二:削峰填谷 (3)相关代码 4.阻塞队列和生产者消费者模型功能的实现 1. 什么是阻塞队列 阻塞队列是一种特殊的队列,和数据结构中普通的队列一样,也遵守先进先出的原则同时,阻塞队列是一种能保证线程安全的数据结构,并且具有以下两种特性:当队列满的时候,继续向队列中插入元素就会让队列阻塞,直到有其他线程从队列中取走元素:当队列为空的时候,继续出队列也会让队列阻塞,直到有其他线程往队列中插入

  • Java中内存异常StackOverflowError与OutOfMemoryError详解

     Java中内存异常StackOverflowError与OutOfMemoryError详解 使用Java开发,经常回遇到内存异常的情况,而StackOverflowError和OutOfMemoryError便是最常遇见的错误. 首先,看看这两种错误的解释: 如果当前线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常. 这里把异常分为两种情况,但是存在一些相互重

  • java 中HttpClient传输xml字符串实例详解

    java 中HttpClient传输xml字符串实例详解 介绍:我现在有一个对象page,需要将page对象转换为xml格式并以binary方式传输到服务端 其中涉及到的技术点有: 1.对象转xml流 2.输出流转输入流 3.httpClient发送二进制流数据 POM文件依赖配置 <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifact

随机推荐