Lock、ReentrantLock和AbstractQueuedSynchronizer的源码要点分析整理

详解Agent Skills:让AI拥有更多专业能力(什么是Agent Skills?如何创建?如何使用?如何获取?) 本文分享如何将零散指令沉淀为可复用的 AI 能力模块🧩,介绍什么是Agent Skills?如何创建?如何获取?📚 阅读详情

        前面已经说了很多Java并发和线程安全的东西,也提到并对比了内在锁和J.U.C包(java.util.concurrent包,后同)中Lock的锁。从这篇开始,对Java并发的整理从理论进入“实践”阶段,本篇对Lock、ReentrantLock和AbstractQueuedSynchronizer源码做简要分析和整理。先从Lock这个interface说起,然后分析ReentrantLock和AQS的实现。

0. 我们先看下Lock接口和ReentrantLock的大体实现。

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

可以看得出来,Lock能做这样几件事:

  • 常规地获得锁
  • 可中断地获得锁
  • 尝试性获得锁,非阻塞
  • 尝试性获得锁,如果超时则返回
  • 解锁
  • 生成和当前锁相关的条件(队列)对象

再来看下ReentrantLock的情况:

  • 类声明,如下实现了Lock和Serializable接口
  • public class ReentrantLock implements Lock, java.io.Serializable
  • 属性,主要的属性只有内部类Sync的对象属性sync,ReentrantLock类的操作实际上都落在了sync身上
  • 构造方法,有重载实现的两个,单参数的方法参数的含义为是否为公平锁,方法的实现就是构造一个Sync对象(根据公平参数,确定是FairSync还是NonfairSync)并赋值给sync,默认构造方法会调用参数为false的方法
  • lock()和newCondition()调用sync的同名方法
  • lockInterruptibly()调用sync的acquireInterruptibly(1)
  • tryLock()调用sync的nonfairTryAcquire(1)
  • 等待性tryLock()调用sync的tryAcquireNanos(1, unit.toNanos(timeout))
  • unlock()调用sync的release(1)

除此之外,ReentrantLock是可重入锁,还有一些支持可重入的方法,这里不细说。可以说ReetrantLock是基于它的内部类Sync的对象来实现的,接下来看下Sync的类层次结构:

Sync类结构

从eclipse中看,类层次结构一目了然,Sync被FairSync和Nonfair扩展,而父层有AbstactOwnableSynchronizer和AbstractQueuedSynchronizer。前者实现了当前同步器被哪个线程占有的逻辑,实现了get/setExclusiveOwnerThread()方法,确定获取当前互斥锁的Thread对象。后者则是java.util.concurrent包中非常重要的类,下面就重点来说说这个AbstractQueuedSynchronizer(AQS)。

1. AQS的队列结构和队列节点

从AbstractQueuedSynchronizer的名字就可以看得出来,这个类是抽象的队列的同步器。同步器不用说了;有关抽象的,以及具体如何和扩展的子类配合实现加锁和解锁,后面那段会具体描述;这里我们看看AQS的比较重要比较核心的部分,也就是状态处理和队列的实现。

从AQS类在eclipse的outline中,可以看出,除了序列化和具体的Unsafe底层操作相关的东西,AQS有三个最重要的属性和两个内部类:

  • private volatile int state
  • private transient volatile Node head
  • private transient volatile Node tail
  • static final class Node
  • public class ConditionObject implements Condition, java.io.Serializable

其中state是当前的锁状态,通常(至少ReentrantLock是这么用的)这是锁是否被占用的一个重要标志,在ReentrantLock实现中是获得锁的重入线程数,0的时候是没有线程占用这个锁的。而和AQS实例绑定(就是非静态的内部类)的ConditionObject类是与条件对列相关的对象,后面细说。剩下的最重要就是Node静态内部类,也是构成队列的主要数据结构。其实此Node实现也并不复杂,就是通常的双向链表结构,有指向前后节点的引用,除此之外就是链表节点的数据部分,有如下属性字段:

  • volatile int waitStatus。当前节点的状态,主要表示当前线程是获得锁、等待锁、在等待队列中等状态,对应于Node类中的几个常量
  • volatile Thread thread。当前节点对应的线程对象。
  • Node nextWaiter。AQS为每个条件对象单独维护了一个等待队列,依靠的就是这个属性引用。

在锁队列维护上,实际上是双向的。每次创建新节点,以当前线程为数据,nextWaiter指向互斥常量或共享常量。新增结点时,获取tail,并设置新节点的prev为tail,并尝试原子操作设置新节点为tail节点,如果tail结点为空或者设置tail结点出问题则调用enq方法循环尝试,其中为空 状态时,则new一个空Node为head,并让tail=head。

出队列的操作实际上是和线程相关的,在阻塞等待获得锁的过程中或者是执行condition的await()时,调用acquireQueued()方法,循环比较当前线程结点的上一个结点是不是head并调用tryAcquire()。如果成功,则设置当前node为head,并解除当前node向前以及前一个结点指向当前node的引用(设置为null),这样前一个结点就失去了引用链上的引用。第一次出队列的是首次初始化队列时创建的空Node对象,后面依次是之前被解锁的线程对应的node。当然,如果tryAcquire()不成功,则会将判断当前node的状态,如果是0则设置为SIGNAL常量并用LockSupport的park()方法挂起当前线程。

2. Sync和AQS的配合以及ReentrantLock的lock()和unlock()实现。
前面简单说到过,ReentrantLock的lock方法调用了sync的lock()方法,而不管是公平实现(FairSync)还是非公平实现(NonfairSync),所做的主要工作都是调用AQS的acquire()方法。而unlock()方法更直接,调用的是AQS的release()方法。

更进一步,对于acquire()和release()方法,所做的大概操作有两样,一个是调用名字为try开头的方法,即tryAcquire()和tryRelease()等,此外就是做队列和线程相关的操作。而对于AQS,有如下五个方法是未完整实现,需要扩展的子类进行定义的:

  • protected boolean tryAcquire(int arg)
  • protected boolean tryRelease(int arg)
  • protected int tryAcquireShared(int arg)
  • protected boolean tryReleaseShared(int arg)
  • protected boolean isHeldExclusively()

结合ReentrantLock及其内部类Sync(以NonfairSync为例)的实现,主要是tryAcquire()和tryRelease(),我们看下如何构造锁操作。

当加锁时,调用acquire()方法,acquire()会尝试原子操作tryAcquire()。这个方法在非公平实现中,主要是通过AQS的state来检查和维护锁状态,如果state是0,说明没有线程占有这个锁,如果不为0并且锁的占有线程是当前线程,则是重入的情况,均可以获得锁并修改state值。如果是首次获得锁,则设置锁占有线程为当前线程。当然,如果前面两种情况都不满足,说明尝试获得锁失败,需要做前面段落所述的队列操作,创建一个等待结点并进入循环,循环中的park()调用挂起当前线程。

当解锁时,做对应而相反的操作。release()调用tryRelease()方法,如果修改state值成功,则找到队列中应该唤起的结点,对节点中的线程调用unpark()方法,恢复线程执行。这个操作在被恢复执行线程acquireQueued()方法的循环中完成,释放头结点并返回是否中断的状态,继续执行。

3. Lock的五个特点方面:尝试性非阻塞获得锁可中断、时间调度、公平性、一对多。

下面在简单介绍下ReentrantLock比起内在的synchronized锁的一些优秀特点的实现:

  • 基于Unsafe的原子操作来修改state的状态,无论成功失败都会直接返回,这保证了非阻塞方式尝试获得锁
  • 可中断和允许时间调度,则是利用了Unsafe的park方法的特性,park掉的线程是可以响应中断被唤醒的,而park的带有时间参数的重载方法则保证了时间调度性
  • 公平和非公平实现,这个是在ReentrantLock的Sync的子类中实现的,主要的区别就是公平锁保证了队列的第一个节点先获得锁,而非公平不保证这点
  • 至于一对多,貌似也没什么可多说的,一个类中可以有多个ReentrantLock类对象属性,自然就可以有多个锁,每个对象单独维护一个state属性

4. Condition的实现。

至于条件队列的实现,前文也多少提到了一些。AQS有个实现了Condition接口的内部类ConditionObject,其复用了锁队列的Node结点,单独为每个条件维护了一个单向链表队列。

当await()时,创建一个状态为CONDITION常量的Node类结点,释放当前线程的锁,并进入一个循环。这个循环退出的条件是结点已经被放到锁队列上或者是检测到了中断做中断处理,循环的内容就是不断的去park()掉当前线程。当循环退出后尝试重新获得锁,以继续执行等待后的代码。

而signal()/signalll()方法更好理解,主要操作就是将一个或者多个Node对象的状态设置为0,并将该节点加入获取锁的队列中,恢复线程。

本文对java.util.concurrent.locks的可重入锁机制和AQS进行了比较详细的分析,后续也有可能会对ReentrantReadWriteLock和Semaphore做分析。更详细的逻辑还请参照JDK的源码。

YOLO26涨点改进 | 小目标损失函数独家改进篇 | TGRS 2025 | 引入AdaFocalLoss损失函数,改进小目标检测,自适应聚焦小目标与样本分布不平衡,提升检测精度与训练稳定性 目录一、前言:YOLO26原生损失函数在小目标检测中的核心痛点(涨点瓶颈)1.1 小目标权重不足,聚焦能力薄弱1.2 样本分布不平衡适配性差,正负样本权重失衡1.3 训练不稳定,易出现梯度消失/震荡1.4 未区分小目标与背景的冗余干扰,误检率高二、TGRS 2025顶刊灵感解析:自适应聚焦损失的核心逻辑三、全网独家创新:AdaFocalLoss损失函数设计与YOLO26适配(核心重点,可复现)3.1 AdaFocalLoss函数整体设计(独家设计,清晰可复现)3.2 各核心模块详细设计(独家创新,可直接复现 阅读详情

相关推荐

别再乱改grub.cfg了!手把手教你用GRUB2自定义多系统启动菜单(含UEFI/BIOS双模式配置)

本文详细介绍了如何安全、高效地使用GRUB2自定义多系统启动菜单,涵盖UEFI/BIOS双模式配置。通过避免直接编辑grub.cfg文件,采用正确的配置方法和命令,帮助用户打造稳定且美观的启动环境,特别适合Linux多系统用户和开发者。

weixin_42553471的博客 590

AbstractQueuedSynchronizer源码

基于队列的抽象同步器,它是jdk中所有显示的线程同步工具的基础,像ReentrantLock/DelayQueue/CountdownLatch等等,都是借助AQS实现的。ConditionObject是专门为AQS服务的,它的节点的构造,状态的标志等都与AQS有关,在wait操作和signal操作时都需要去操作AQS的同步队列。ConditionObject实现了在基于AQS锁的情况下对获取到锁的线程进行有条件的等待和唤醒,其主要的方法是await和signal以及它们的变种。

我的博客 418

永不消逝的电波 -GSM空口信号的接收与解码

一开始接触软件无线电应该是从很早之前对RFID洗衣卡的解密开始的,后来接触人员定位,慢慢的打开了这样一扇大门。。。。 翻到之前做的GSM信号的接收解码,今天就对其过程做一个记录,也不是什么新颖的内容,权当是记录和回忆了。 对于GSM网络的相关内容,不是非常的熟悉,如有不正确的,欢迎批评指正。

Jack.Xu 专栏 1万+

队列同步器AbstractQueuedSynchronizer简介

Lock接口 锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时 访问共享资源(但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁)。在Lock接 口出现之前,Java程序是靠synchronized关键字实现锁功能的,而Java SE 5之后,并发包中新增 了Lock接口(以及相关实现类)用来实现锁功能,它提供了与synchronized关键字类似的同步功 能,...

田野中的锋芒 856

Xilinx同步FIFO IP核的两种读模式对比:Standard vs FWFT(附Vivado仿真代码)

本文深入解析Xilinx同步FIFO IP核的两种读模式——Standard与FWFT,通过时序仿真和Vivado代码示例对比其性能差异。Standard模式适合严格流水线设计,而FWFT模式在低延迟场景表现更优。文章包含详细的testbench搭建指南和性能实测数据,帮助FPGA工程师优化数据流设计。

table的专栏 346

计算机考研常见面试题

写在前面 本人参加20考研,由于复试没有具体要求会问哪几门课,所以我就找了网上一些常见的计算机专业的面试题,整理如下 主要结构: 数据库 软件工程 操作系统 计算机组成原理 计算机网络 数据结构 C++ Java 数据库 1、数据:数据库中存储的基本对象。 数据库:长期存储在计算机内的、有组织的、可共享的数据集合。 数据库管理系统:专门用于建立和管理数据库的一套软件。对外部程序提供访问数据库的方法,对内进行数据管理。 数据库系统:与和数据库相关的整个系统,一般由数据库、数据库管理系统、应用程序、数

qq_38841618的博客 2万+

AbstractQueuedSynchronizer 源码解析

总综上,head 在并发的过程中是可能被别的线程修改的,所以需要 h == null 和 (h = head) == null 的判断,同时为了防止节点都能被唤醒,所以 head.waitStatus = 0 的情况下 设置 head.waitStatus = PROPAGATE,并且 在 setHeadAndPropagate 方法里通过 h.waitStatus < 0 的判断避免节点出现无法唤醒的情况,不得不说,设计真的非常巧妙。Node 节点就是用来包装一个个的线程的。

weixin_45278293的博客 1238

Android BLE扫描指南:解决设备发现问题

假设你正在开发一个MAUI应用,用于扫描并连接到BLE设备。你的代码在Windows上运行得很好,但在Android上却无法发现任何设备。尽管你已经在中声明了必要的权限,并且在运行时请求这些权限,扫描还是无法工作。

m0_62153576的博客 224

AbstractQueuedSynchronizer 源码详解(非公平)

在AQS代码中有非常多的unsafe类的操作,其中的提供原子性的内存操作,是实现无锁数据结构(如 AtomicInteger)的基础。// 此处代码使用静态代码块的方式将 共享变量 state,队列头节点 head,// 尾节点 tail,节点状态 waitStatus,以及 next 指针// 的内存地址偏移量 赋值给了这5个静态变量,方便后面进行CAS操作。

m0_61556498的博客 838

使用Mask2Former和X-Decoder训练自定义数据集及结果复现报告

本报告记录了使用MMDetection框架中的Mask2Former和X-Decoder模型在自定义数据集上的训练过程。报告包含环境配置、数据集准备、模型训练与优化(解决了Mask2Former的PQ值异常问题)、性能评估等关键环节。详细展示了从数据集转换(将客户提供的COCO格式数据适配MMDetection框架)、配置文件修改、到最终模型训练和评估的全流程。特别针对Mask2Former训练中的PQ值异常问题提供了解决方案,并对比了两种模型在自定义数据集上的表现。报告还包含完整的代码实现和技术细节。

huanghm88的专栏 1094

【思考模型框架】采用双钻模型:发散(桌面研究/用户访谈)→收敛(需求归类/优先级排序),输出需求规格书、追踪矩阵及产品待办清单。

设计思维双钻模型是一种以用户为中心的需求管理方法,通过"发散-收敛"的双循环确保问题定位准确、方案切实可行。第一钻石(探索→定义)聚焦"找对问题",运用用户访谈、田野调查等方法挖掘真实需求;第二钻石(开发→交付)解决"做对方案",通过原型测试、A/B测试验证可行性。该模型强调低保真验证、多团队协作和真实用户测试,避免资源浪费与伪需求开发。典型应用场景包括产品功能优化、用户体验提升等需要精准落地的需求场景。核心价值在于建立从用户痛点到解决方案的可控闭

奶爸的编程之路,也就一周冷个三天 319

AQS(同步器) 转

Java.util.concurrent.locks.AbstractQueuedSynchronizer 简介 提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架。该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础。使用的方法是继承,子类通过继承同步器并需要实现它的方法来管理其状态,管理的方式就是通过类似acquire和rel...

sunqi 369

一文读懂OpenClaw:核心组件与工作原理深度解析

Agent核心:负责理解意图、规划任务、协调执行消息系统:统一的消息格式,实现组件间解耦工具注册表:可扩展的工具系统,支持自定义工具模型适配层:多模型支持,统一API接口安全机制:沙箱隔离、权限确认、操作审计性能优化:并行执行、智能缓存、增量更新理解这些核心组件和工作原理,将帮助你更好地使用OpenClaw,也为后续开发自定义扩展打下坚实基础。在下一篇文章中,我们将详细介绍OpenClaw的界面操作,包括命令行与交互模式的各种技巧。

gaoxiang_120的专栏 833

AQS(AbstractQueuedSynchronizer)详解与源码分析

文章目录AQS简介同步队列独占锁独占锁的获取(acquire方法)独占锁的释放(release()方法)可中断式获取锁(acquireInterruptibly方法)超时等待式获取锁(tryAcquireNanos()方法)共享锁共享锁的获取(acquireShared()方法)共享锁的释放(releaseShared()方法)可中断(acquireSharedInterruptibly()方法)...

ThinkWon的博客 1万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

ReentrantLock源码分析

ReentrantLock源码实现分析整理 重入锁ReentrantLock实现了Lock接口,即实现了加解锁操作的基本API,在此基础上,ReentrantLock提供以下特性: 1.支持同一线程对资源的重复加锁,即同一线程重复调用lock方法不会堵塞,但后续解锁次数需要和加锁次数同步,才能真正意义地释放锁。 2.支持获取锁时的公平和非公平性选择, 类的内部结构: 在ReentrantLock,有3个非常核心的内部类:Sync,FairSync,NonfairSync,其中Sync继承自Abstract

lf19960114的博客 127

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

ReentrantLock源码探究、探究公平锁与非公平锁背后的奥秘

由于疫情,加上忙于工作的原因,也是有段时间没有写博客了,本文是基于以前写过的博客再整理出来的,本着加深理解的原则,发现以前的很多文章重新去温习的时候,读起来有点晦涩,于是萌生了再整理的想法,同时加了一个面试专栏,让大家各取所需首先简单介绍几个概念重量级锁:用户起了几个线程,经过os调度,然后在交给java虚拟机执行。重量级锁是操作os函数来解决线程同步问题的,涉及到了内核态与用户态之间的切换,这个开销是很大的,因此被称为重量级锁。轻量级锁。

风团团 1215

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【JDK1.8源码阅读】 JUC锁基础源码实现分析整理(四)

JUC(java.util.concurrent)下定义了JDK关于锁相关的框架。整体锁框架可参照参考文章的一张图: 对其中每个工具类介绍如下: Lock Lock为接口类型,Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition对象。 Lock接口的基本API定义如下: publi...

总结沉淀 517

从ReentrantLock角度解析AQS

通过上面的描述可以发现,资源解锁是相对简单的;它只能被上锁的线程去解锁;通过递减AQS内部维护的state属性值,直到state减为0,表示资源已被解锁;当资源被解锁后,需要通过Unsafe的unpark方法,去唤醒CLH队列中,被挂起的第一个节点上的线程。Semaphore等。AQS是一个抽象类,主要是通过继承的方式来使用,它本身没有实现任何的同步接口,仅仅是定义了同步状态的获取以及释放的方法来提供自定义的同步组件。可以这么说,只要搞懂了AQS,那么J.U.C中绝大部分的api都能轻松掌握。

csdn1234561231的博客 531

AQS源码分析看这一篇就够了

关于面试刷题也是有方法可言的,建议最好是按照专题来进行,然后由基础到高级,由浅入深来,效果会更好。Java基础部分算法与编程数据库部分流行的框架与新技术(Spring+SpringCloud+SpringCloudAlibaba)这份面试文档当然不止这些内容,实际上像JVM、设计模式、ZK、MQ、数据结构等其他部分的面试内容均有涉及,因为文章篇幅,就不全部在这里阐述了。

2401_84691713的博客 935

深入JUC源码看锁

锁,相信大家都听过也用过。如下伪代码: Lock lk = new Lock(); try{ lk.lock(); // 不为人知的代码块… … } finally { lk.unLock(); } FAQ: 用起来简单丝滑有没有?但是请仔细思考一下如下几个问题: 什么场景下用它? 它帮我们解决了啥问题? 为啥区区一个锁就能解决这个问题,它为何这么diao,用了啥技术? 一般情况下只有在“并发”访问“共享”资源情况下,...

PYXLY1314的专栏 560

java并发编程笔记之详解ReentrantLock

ReentrantLock是可重入的互斥锁,虽然具有与synchronized相同功能,但是会比synchronized更加灵活。ReentrantLock底层基于AbstractQueuedSynchronizer实现。

DarrenHoo的博客 272
上一篇: AbstractQueuedSynchronizer在工具类ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier中的应用
下一篇: Callable、Future和CompletionService
kobejayandy
博客等级 码龄18年 2853粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值