JUC基础类-AbstractQueuedSynchronizer

AbstractQueuedSynchronizer

  • 1、AbstractQueuedSynchronizer概述
  • 2、AbstractQueuedSynchronizer源码分析
    • 2.1 AQS源码
    • 2.2 Node类

如有侵权,请联系~
如有问题,也欢迎批评指正~

1、AbstractQueuedSynchronizer概述

AbstractQueuedSynchronizer(抽象队列同步器,简称 AQS)是 Java 并发包中的一个重要类,它为实现基于队列的同步器提供了一个框架。AQS 主要用于构建各种同步机制,比如锁、信号量、读写锁等。它通过一个 FIFO 队列(先进先出)来管理线程的获取和释放同步状态(如锁定状态),从而有效地处理多线程之间的竞争。
JUC包下的lock锁子包,以及JUC下的阻塞队列ArrayBlockingQueue、CountDownLatch等等这些常用的类都是基于AQS实现的。

2、AbstractQueuedSynchronizer源码分析

2.1 AQS源码

AQS使用的管程模型,本篇文章作为JUC-locks锁的基础补充。首先先看下AQS的架构图:
在这里插入图片描述
AQS的源码注释【AQS主要是一个队列(线程阻塞队列,等待资源)和一个共用争取资源状态(state),方法都是围绕着阻塞队列以及资源的】:

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

	// 上述的阻塞队列的头
    private transient volatile Node head; // 阻塞队列头,每个阻塞线程都被封装成node节点
	// 阻塞队列尾
    private transient volatile Node tail;  
	 // 表示争夺资源目前的状态。
	 // 值为0:表示该资源空闲 
	 // 值大于0:表示被n个线程所持有【例如:CountDownLatch】或者一个线程被持有n次【例如:可重入锁】
    private volatile int state;

	// 当前持有该资源的线程,这个属性来自于父类AbstractOwnableSynchronizer
	private transient Thread exclusiveOwnerThread;  
	
	// 获取资源 addWaiter(Node.EXCLUSIVE), arg)将当前线程放在阻塞队列中
	public final void acquire(int arg) {
	    if (!tryAcquire(arg) &&
	        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
	        selfInterrupt();
	}
	
	// 当前线程会判断当前是不是阻塞队列的第一个并且是否能获取到资源。如果能够获取到资源则返回,如果获取不到则等待。
	// 并且在等待过程中是否被中断也会将作为结果返回
	 final boolean acquireQueued(final Node node, int arg) {
	    boolean failed = true;
	    try {
	        boolean interrupted = false;
	        for (;;) {
	            final Node p = node.predecessor();
	            if (p == head && tryAcquire(arg)) {
	                setHead(node);
	                p.next = null; // help GC
	                failed = false;
	                return interrupted;
	            }
	            if (shouldParkAfterFailedAcquire(p, node) &&
	                parkAndCheckInterrupt())
	                interrupted = true;
	        }
	    } finally {
	        if (failed)
	            cancelAcquire(node);
	    }
	}
	
	// 使用UnSafe类的CAS+自旋 将node节点加入到 AQS队列的末尾
	private Node enq(final Node node) {
	    for (;;) {
	        Node t = tail;
	        if (t == null) { // Must initialize
	            if (compareAndSetHead(new Node()))
	                tail = head;
	        } else {
	            node.prev = t;
	            if (compareAndSetTail(t, node)) {
	                t.next = node;
	                return t;
	            }
	        }
	    }
	} 
	
	// 方法中带tryXXX的都会响应中断,不带的不响应中断
	// 这个方法是获取对象的锁,争夺资源。所以这个交给实现类进行操作。例如公平锁和非公平锁就是通过这个方法实现体现的
	// arg一般是获取资源的数量,这个方法尝试非阻塞地获取资源,如果成功就立即返回true,如果失败就立即返回false,不会将线程加入到等待队列,也不会使线程阻塞。
	 protected boolean tryAcquire(int arg) {
	    throw new UnsupportedOperationException();
	}
	
	// 这个方法尝试在给定的时间内获取资源,如果在指定的时间内成功获取到资源就立即返回true,如果在指定的时间内没有获取到资源就返回false。这个方法也是阻塞的,如果在阻塞状态下被中断,它会响应中断,抛出InterruptedException,并返回false。
	public final boolean tryAcquireNanos(int arg, long nanosTimeout)
	        throws InterruptedException {
	    if (Thread.interrupted())
	        throw new InterruptedException();
	    return tryAcquire(arg) ||
	        doAcquireNanos(arg, nanosTimeout);
	}
	
	private boolean doAcquireNanos(int arg, long nanosTimeout)
	        throws InterruptedException {
	    if (nanosTimeout <= 0L)
	        return false;
	    final long deadline = System.nanoTime() + nanosTimeout;
	    final Node node = addWaiter(Node.EXCLUSIVE);  // 添加到阻塞队列队尾
	    boolean failed = true;
	    try { // 如果等待时间超过spinForTimeoutThreshold,则线程挂起等待被唤醒;否则在这for自旋进行判断。这样剩去了线程切换的时间
	        for (;;) {
	            final Node p = node.predecessor();
	            if (p == head && tryAcquire(arg)) { //如果这个线程在对头并且尝试一下并获取到资源,直接返回
	                setHead(node);
	                p.next = null; // help GC
	                failed = false;
	                return true;
	            }
	            nanosTimeout = deadline - System.nanoTime();
	            if (nanosTimeout <= 0L)  // 到时间还没获取到资源,失败
	                return false;
	            if (shouldParkAfterFailedAcquire(p, node) &&
	                nanosTimeout > spinForTimeoutThreshold) //spinForTimeoutThreshold=1000
	                LockSupport.parkNanos(this, nanosTimeout);  // 如果等待时间比较久,那就使该线程进行阻塞。当等待时间超过spinForTimeoutThreshold时,该线程挂起等待超时被唤醒
	            if (Thread.interrupted())
	                throw new InterruptedException();
	        }
	    } finally {
	        if (failed)
	            cancelAcquire(node);
	    }
	}
	
	// AQS尝试获取和尝试释放都未实现,等待子类实现
	protected boolean tryRelease(int arg) {
	    throw new UnsupportedOperationException();
	}
}

2.2 Node类

Node类对应于上述中阻塞队列中的node。这里比较重要的是每个node的状态标识:

static final class Node {
  			static final Node SHARED = new Node();  // 表示这个node(线程)对资源是共享的还是需要独占的
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;    // 该node的线程被取消了(可能超时或者中断导致的)
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;    // 后继的node中的线程需要被其唤醒
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;   // 当前node节点在条件队列中
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;   // 共享模式下的节点释放资源后应该将信息传播给其他节点

        /**
         * Status field, taking on only the values:
         *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.
         *   CANCELLED:  This node is cancelled due to timeout or interrupt.
         *               Nodes never leave this state. In particular,
         *               a thread with cancelled node never again blocks.
         *   CONDITION:  This node is currently on a condition queue.
         *               It will not be used as a sync queue node
         *               until transferred, at which time the status
         *               will be set to 0. (Use of this value here has
         *               nothing to do with the other uses of the
         *               field, but simplifies mechanics.)
         *   PROPAGATE:  A releaseShared should be propagated to other
         *               nodes. This is set (for head node only) in
         *               doReleaseShared to ensure propagation
         *               continues, even if other operations have
         *               since intervened.
         *   0:          None of the above
         *
         * The values are arranged numerically to simplify use.
         * Non-negative values mean that a node doesn't need to
         * signal. So, most code doesn't need to check for particular
         * values, just for sign.
         *
         * The field is initialized to 0 for normal sync nodes, and
         * CONDITION for condition nodes.  It is modified using CAS
         * (or when possible, unconditional volatile writes).
         */
        volatile int waitStatus;  // node中线程的状态,取值为上面的值

        /**
         * Link to predecessor node that current node/thread relies on
         * for checking waitStatus. Assigned during enqueuing, and nulled
         * out (for sake of GC) only upon dequeuing.  Also, upon
         * cancellation of a predecessor, we short-circuit while
         * finding a non-cancelled one, which will always exist
         * because the head node is never cancelled: A node becomes
         * head only as a result of successful acquire. A
         * cancelled thread never succeeds in acquiring, and a thread only
         * cancels itself, not any other node.
         */
        volatile Node prev;   // 前一个node节点指针

        /**
         * Link to the successor node that the current node/thread
         * unparks upon release. Assigned during enqueuing, adjusted
         * when bypassing cancelled predecessors, and nulled out (for
         * sake of GC) when dequeued.  The enq operation does not
         * assign next field of a predecessor until after attachment,
         * so seeing a null next field does not necessarily mean that
         * node is at end of queue. However, if a next field appears
         * to be null, we can scan prev's from the tail to
         * double-check.  The next field of cancelled nodes is set to
         * point to the node itself instead of null, to make life
         * easier for isOnSyncQueue.
         */
        volatile Node next;   // 后一个node的指针

        /**
         * The thread that enqueued this node.  Initialized on
         * construction and nulled out after use.
         */
        volatile Thread thread;   // 等待资源的线程

        /**
         * Link to next node waiting on condition, or the special
         * value SHARED.  Because condition queues are accessed only
         * when holding in exclusive mode, we just need a simple
         * linked queue to hold nodes while they are waiting on
         * conditions. They are then transferred to the queue to
         * re-acquire. And because conditions can only be exclusive,
         * we save a field by using special value to indicate shared
         * mode.
         */
        Node nextWaiter;  // 指向下一个条件等待的node。所以,猜想一个等待队列和一个条件等待队列
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/915359.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文献阅读 | Nature Methods:使用 STAMP 对空间转录组进行可解释的空间感知降维

文献介绍 文献题目&#xff1a; 使用 STAMP 对空间转录组进行可解释的空间感知降维 研究团队&#xff1a; 陈金妙&#xff08;新加坡科学技术研究局&#xff09; 发表时间&#xff1a; 2024-10-15 发表期刊&#xff1a; Nature Methods 影响因子&#xff1a; 36.1&#xff0…

Redis系列之底层数据结构ZipList

Redis系列之底层数据结构ZipList 实验环境 Redis 6.0 什么是Ziplist&#xff1f; Ziplist&#xff0c;压缩列表&#xff0c;这种数据结构会根据存入数据的类型和大小&#xff0c;分配大小不同的空间&#xff0c;所以是为了节省内存而采用的。因为这种数据结构是一种完整连续…

界面控件DevExpress WPF中文教程:TreeList视图及创建分配视图

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

数据结构中数据有序性/ 单调性 ——二分查找

以下记录的都是闭区间写法 例题&#xff1a;34. 在排序数组中查找元素的第一个和最后一个位置 1.关系转换 寻找目标值有四种情况&#xff1a;≥、>、≤、< 比如目标值x&#xff0c; 可以转化为 ≥x、≥ x1、≤x、≤ x1 比如数组大小为6&#xff0c;目标值为…

探索Python的HTTP利器:Requests库的神秘面纱

文章目录 **探索Python的HTTP利器&#xff1a;Requests库的神秘面纱**一、背景&#xff1a;为何选择Requests库&#xff1f;二、Requests库是什么&#xff1f;三、如何安装Requests库&#xff1f;四、Requests库的五个简单函数使用方法1. GET请求2. POST请求3. PUT请求4. DELET…

《Linux从小白到高手》综合应用篇:深入详解Linux swap及其调整优化

1. 引言&#xff1a; Swap是存储设备上的一块空间&#xff08;分区&#xff09;&#xff0c;操作系统可以在这里暂存一些内存里放不下的东西。这从某种程度上相当于增加了服务器的可用内存。虽然从swap读写比内存慢&#xff0c;但总比没有好&#xff0c;算是内存不足时一种比较…

SpringMVC学习笔记(一)

一、SpringMVC的基本概念 &#xff08;一&#xff09;三层架构和MVC 1、三层架构概述 我们的开发架构一般都是基于两种形式&#xff0c;一种是 C/S 架构&#xff0c;也就是客户端/服务器&#xff0c;另一种是 B/S 架构&#xff0c;也就是浏览器服务器。在 JavaEE 开发中&…

小面馆叫号取餐流程 佳易王面馆米线店点餐叫号管理系统操作教程

一、概述 【软件资源文件下载在文章最后】 小面馆叫号取餐流程 佳易王面馆米线店点餐叫号管理系统操作教程 点餐软件以其实用的功能和简便的操作&#xff0c;为小型餐饮店提供了高效的点餐管理解决方案&#xff0c;提高了工作效率和服务质量 ‌点餐管理‌&#xff1a;支持电…

【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--角色可访问接口管理

咱们继续来编写孢子记账的简易权限&#xff0c;这篇文章中我们将编写角色可访问接口的管理API&#xff0c;同样我不会把完整的代码全都列出来&#xff0c;只会列出部分代码&#xff0c;其余代码我希望大家能自己手动编写&#xff0c;然后对比项目代码。废话不多说&#xff0c;开…

Linux上Python使用MySQLdb包连接MySQL5.7和MySQL8的问题

在一台安装有MySQL8的Linux上用MySQLdb包连接MySQL5.7&#xff0c;连接参数中加上ssl_mode‘DISABLED’,能正常连接&#xff1b;不加ssl_mode参数&#xff0c;会报 而在连接MySQL8时加不加ssl_mode都能正常连接&#xff0c;但在使用过程&#xff0c;加了ssl_mode参数&#xff…

列表(list)

一、前言 本次博客主要讲解 list 容器的基本操作、常用接口做一个系统的整理&#xff0c;结合具体案例熟悉自定义内部排序方法的使用。如有任何错误&#xff0c;欢迎在评论区指出&#xff0c;我会积极改正。 二、什么是list list是C的一个序列容器&#xff0c;插入和删除元素…

spring使用xml文件整合事务+druid+mybatis

1.事务 事务&#xff08;Transaction&#xff09;是数据库管理系统中的一个重要概念&#xff0c;它表示一组不可分割的操作序列&#xff0c;这些操作要么全部执行成功&#xff0c;要么全部不执行&#xff0c;以确保数据库从一个一致性状态转换到另一个一致性状态。事务具有以下…

大语言模型LLM综述

一、LM主要发展阶段 1.1、统计语言模型SLM 基于统计学习方法&#xff0c;基本思想是基于马尔可夫假设HMM建立词概率预测模型。如n-gram语言模型 1.2、神经语言模型NLM 基于神经网络来做词的分布式表示。如word2vec模型 1.3、 预训练语言模型PLM 预训练一个网络模型来做词表…

【Jenkins实战】Windows安装服务启动失败

写此篇短文&#xff0c;望告诫后人。 如果你之前装过Jenkins&#xff0c;出于换域账号/本地帐号的原因想重新安装&#xff0c;你大概率会遇上一次Jenkins服务启动失败提示&#xff1a; Jenkins failed to start - Verify that you have sufficient privileges to start system…

Linux kernel 堆溢出利用方法(二)

前言 本文我们通过我们的老朋友heap_bof来讲解Linux kernel中off-by-null的利用手法。在通过讲解另一道相对来说比较困难的kernel off-by-null docker escape来深入了解这种漏洞的利用手法。&#xff08;没了解过docker逃逸的朋友也可以看懂&#xff0c;毕竟有了root权限后&a…

微服务(一)

目录 1.认识微服务 1.1.单体架构 1.2.微服务 1.3.SpringCloud SpringCloud版本 SpringBoot版本 2.服务注册和发现 2.1.注册中心原理 2.2.Nacos注册中心 2.3.服务注册 2.3.1.添加依赖 2.3.2.配置Nacos 2.4.服务发现 2.4.1.引入依赖 2.4.2.配置Nacos地址 2.4.3.发…

ubontu--cuDNN安装

1. 下载 cuDNN https://developer.nvidia.com/cudnn 2. 拷贝到服务器/home/<username>文件夹下 解压缩到当前文件夹&#xff1a; tar -xvf cudnn-linux-x86_64-9.5.1.17_cuda11-archive.tar.xz复制头文件和库文件到cuda安装目录/usr/local/cuda/ sudo cp /home/usern…

Vue 批量注册组件实现动态组件技巧

介绍 Vue 动态组件的应用场景很多,可应用于动态页签,动态路由等场景,其核心原理是批量注册。在Vue2和Vue3中实现原理相同,只是语法略有差异。 Vue2 实现 基于 webpack require.context() 是webpack提供的一个自动导入的API 参数1&#xff1a;加载的文件目录 参数2&#xff…

WEB攻防-通用漏洞SQL读写注入MYSQLMSSQLPostgraSQL

知识点&#xff1a; 1、SQL注入-MYSQL数据库&#xff1b; 2、SQL注入-MSSQL数据库&#xff1b; 3、SQL注入-PostgreSQL数据库&#xff1b; 首先要找到注入点 详细点&#xff1a; Access无高权限注入点-只能猜解&#xff0c;还是暴力猜解 MYSQL&#xff0c;PostgreSQL&am…

NocoBase 本周更新汇总:提升工作流易用性

汇总一周产品更新日志&#xff0c;最新发布可以前往我们的博客查看。 NocoBase 目前更新包括两个分支&#xff1a;main 和 next 。 main &#xff1a;截止目前最稳定的版本&#xff0c;推荐安装此版本。 next&#xff1a;内测版&#xff0c;包含一些未发布的新特性&#xff…