Java并发编程(四)—基于队列同步器的可重入锁

Java并发编程札记-(四)JUC锁-03AQS AQS,AbstractQueuedSynchronizer的缩写,是JUC中非常重要的一个类。javadoc中对其的介绍是: 为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量、事件,等等)提供一个框架。此类的设计目标是成为依靠单个原子 int 值来表示状态的大多数同步器的一个有用基础。 AbstractQueuedSynchronizer是CountDownL 阅读详情

前言

在Java 1.5版本的时候,Java提供了新的同步机制—Lock接口,通过Lock接口可以实现不同类型的锁来支持多线程的同步。相比较synchronized关键字,通过Lock实现的锁可以更好的控制同步块的粒度。 在Lock子类的内部实现中,锁是通过Java的队列同步器实现的(AbstractQueuedSynchronizer),不熟悉队列同步器的同学,可以参考我的另一篇博客[Java并发编程(三)—队列同步器(AbstractQueuedSynchronizer)](https://blog.youkuaiyun.com/programerxiaoer/article/details/81463219)

关于ReentrantLock的原理分析

ReentrantLock是Lock接口的一个子类,它是一种可重入锁,也就表示持有该锁的线程可以对资源重复加锁。除此之外,可重入锁也支持公平锁和非公平锁。
ReentrantLock的可重入特性
可重入是指的线程在获取锁之后能够再次获取到该锁并且不会阻塞。我们都知道在队列同步器通过模板方法tryAcquire获取锁,并且通过一个int类型的状态标志来标识锁是否获取成功,而实现可重入的特性就需要依赖状态标志。
final Thread current = Thread.currentThread();
            int c = getState();  //状态标志
            if (c == 0) {  //c==0时尝试获取锁
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) { //在此获取锁时,如果是当前线程,则可以继续获取锁
                int nextc = c + acquires; //更新状态标志
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true; // 成功获取锁
            }
            return false;

以上就是ReentrantLock的可重入特性的原理实现。通过状态标志,当状态标志为0时,表示当前线程还未获取锁,然后尝试获取锁。如果是当前线程在此获取锁时,更新状态标志。
我们可以看到只有状态标志为0时,线程才能尝试获取锁,那么可重入锁在获取锁n次后,相应的也需要释放n次所。

 protected final boolean tryRelease(int releases) {
            int c = getState() - releases; // 释放锁
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) { //状态标志为0时,释放锁成功
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
ReentrantLock的公平锁与非公平锁
ReentrantLock还分为公平锁和非公平锁。其中公平锁就是线程获取锁的顺序遵循FIFO,也就是在等待队列中,线程安装先进先出的规则获取锁。而非公平锁只要队列中的线程能够设置同步状态成功就可以获取锁,也就是线程获取锁时根据CPU调度的。所以非公平锁可能导致线程“饥饿”问题,是的其他线程能够得到的时间片较少。但是使用非公平锁时,线程的切换次数要少于使用公平锁,所以效率也相应较高。
公平锁的实现
ReentrantLock中公平锁也是通过队列同步器提供的模板方法实现锁机制。
static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        @ReservedStackAccess
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            	// 这里就是实现公平锁的关键
            	//hasQueuedPredecessors()的作用就是判断当前线程在队列中是否有前置节点
            	//没有的情况下才能尝试获取锁
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
非公平锁的实现
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            	//可以看到非公平锁与公平锁的差别只是没有了hasQueuedPredecessors()的判断条件
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

从上面的代码可以看出,在代码实现上公平锁与非公平锁的差别只是判断了尝试获取锁的线程在队列中是否有前置节点。如果有的话,在公平锁的情况下就不能获取锁,非公平锁的情况下可以获取锁。

总结

通过以上的分析,可以看到锁(Lock)的实现并不复杂。这是因为Java提供了队列同步器,在队列同步器中实现了锁获取同步状态的机制,不同特性的锁只需要实现相应的模板方法即可,同时实现相应的获取与释放逻辑就可以完成锁的实现。这也开发者提供了更多的选择,让开发者也能够轻松的实现自己的锁。 除了Lock的实现,一些同步工具类也是基于队列同步器实现的,比如:CountDownLatch、CyclicBarrier等。
Java 并发编程:ReentrantLock 锁与 AQS 大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 030 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自己的技术栈的同学。与此同时,本专栏的所有文章,也都会准备充足的代码示例和完善的知识点梳理,因此也十分适合零基础的小白和要准备工作面试的同学学习。 阅读详情

相关推荐

python DEA: 基于非径向距离NDDF的Malmquist-Luenberger 指数及其分解

点赞发Nature 关注中Science Malmquist-Lenberger指数(ML指数)是距离函数与DEA计算中常用的指数,ML指数可以进一步分解为技术进步technological progress和效率改善effeciency change. 技术进步常被当做增长模型中的全要素生产率,用到许多计量经济研究中,探讨影响技术进步的环境变量。这里我分享一个考虑非期望产出下的NDDF方法计算ML指数。 该ML指数为global ML(区别于sequential ML) import numpy as

Cmmmmm丶的博客 5295

被问到可重入锁条件队列,看这一篇就够了!|原创

本文深入解读了高频面试点——ReentrantLock的条件队列使用方法及其原理。源码有详细注释,建议收藏阅读。点击上方“后端开发技术”,选择“设为星标” ,优质资源及时送达Jdk中独占锁的实现除了使用关键字synchronized外,还可以使用ReentrantLock。虽然在性能上两者没有什么区别,但ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更...

Daniel的博客 874

Super4PCS文章阅读笔记

Super4PCS阅读笔记 Super4PCS阅读笔记 前言 前言 今天给大家分享一下点云配准四点法4PCS0的变种——Super4PCS。此算法的原文在:Super4PCS: Fast Global Pointcloud Registration via Smart Indexing。稍微我也会写一篇关于如何使用这篇文章的里所提供的代码进行点云粗配准。关于四点法...

Vodake的博客 4539

温习线可重入锁、阻塞队列、线程池的原理

直接看代码注释,很好理解的! Lock package com.wen.java.concurrent.mylock; /* * 可重入锁原理 */ public class Lock { //被锁标识 boolean isLocked = false; //获得当前锁的线程 Thread lockedBy = null; //被锁的次数...

小浩子的博客 602

手把手教你用CubeMX给GD32F103点灯、调串口(附完整工程)

本文详细介绍了如何使用CubeMX为GD32F103开发板配置GPIO点灯和串口通信,包含完整的工程创建、代码构建和测试流程。特别强调了GD32与STM32的差异点及常见避坑技巧,帮助开发者快速上手GD32开发。

weixin_42566424的博客 313

深入解析Vivado FIFO的Standard与FWFT模式:性能对比与应用场景

本文深入解析了Vivado FIFO的Standard与FWFT两种工作模式的核心差异。通过对比数据就绪时机、读延迟、控制逻辑和有效深度等关键特性,并结合图像处理、数据包转发等典型应用场景,为FPGA设计者提供了清晰的选择依据和实战配置指南,帮助优化数据流性能与设计效率。

weixin_29219189的博客 253

队列同步器的分析

包括:同步队列,独占式同步状态获取与释放、共享式同步状态获取与释放 1.同步队列 AQS依赖内部的同步队列(一个FIFO双向队列)来完成同步状态管理。当线程获取同步状态失败时,同步器会将当前线程和等待状态等信息包装成一个节点并将其加入同步队列,同时会阻塞当前线程。当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。 节点是构成同步队列的基础,同步器拥有首节点和尾节点,没有成功...

qq_33827112的博客 315

YOLOv11 改进 - 检测头 | Detect_LSCD轻量共享卷积检测头:轻量化设计破解计算瓶颈,增强复杂场景目标感知能力

本文介绍了基于群归一化(GroupNorm)和共享卷积的轻量化检测头LSCD,并将其集成进YOLOv11。LSCD检测头充分利用了GroupNorm和共享卷积的优势,在保持特征信息有效融合的同时,减少计算量和运算复杂度。我们将LSCD的代码集成到YOLOv11检测头中,并在tasks文件中进行注册。实验证明,YOLOv11-LSCD在目标检测任务中取得了良好的效果。

魔改工程师的博客 2386

rabbitmq之消息重入队列

说起消息重入队列还得从队列注册消费者说起,客户端在向队列注册消费者之后,创建的channel也会被主队列进程monitor,当channel挂掉后,主队列进程(rabbit_amqqueue_process)收到'DOWN'通知,将未ack的消息重入队列,并根据消息的deliver tag,也就是消费入队列的顺序,将消息重入队列中 主要代码如下: 1.注册消费者 handle_metho...

weixin_30418341的博客 423

【云原生系列】第一讲:什么是云计算

云计算(cloud computing):分布式:分布式计算的一种,指的是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序,合并&反馈:通过多部服务器组成的系统进行处理和分析这些小程序得到结果并返回给用户。云计算早期:分布式计算:简单的分布式计算,解决任务分发,并进行计算结果的合并。因而,云计算又称为网格计算。通过分布式计算,可以在很短的时间内(几秒钟)完成对数以万计的数据的处理,从而达到强大的网络服务。现阶段:计算:分布式计算、效用计算、负载均衡、并行计算、存储。

颜淡慕潇 1万+

ubuntu emto-cpa安装与编译

ubuntu emto-cpa安装与编译

qq_45834835的博客 1436

队列同步器(AQS)详解

Java自定义同步组件的设计分析、设计思路与源码分析、AQS(AbstractQueuedSynchronizer)结构与原理

void 1万+

大学计算 - 方程求解 实训答案 第3关

【代码】大学计算 - 方程求解 实训答案 第3关。

2403_89314916的博客 106

java并发编程面试题精讲——day02

本文摘要:文章详细探讨了Java多线程编程中的核心概念和技术,包括锁机制(synchronized、ReentrantLock)、线程安全(volatile、CAS)、线程池以及常见并发问题解决方案。重点分析了synchronized的锁升级过程、AQS框架实现原理、ThreadLocal内存泄漏问题、乐观锁与悲观锁区别等关键知识点,并提供了实际场景下的应用示例和优化建议,如使用CountDownLatch控制线程执行顺序、合理配置线程池参数等。全文系统性地梳理了Java并发编程的核心技术点和常见面试问题。

2401_84526799的博客 373

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Java并发编程:用AQS写一把可重入锁

测试类 /** * MyAqsLock的测试类 */ public class Main { private int value; private Lock lock = new MyAqsLock(); private Lock lock1 = new ReentrantLock(); public int increment() { ...

张翠山的博客 6723

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【Java中的锁】队列同步器(AQS)详解

AQS即的简称,翻译过来就是抽象队列同步器的意思,它使用一个int成员变量表示同步状态,通过内置的FIFO队列来完成想要获取资源的线程的排队工作,是实现大部分同步需求的基础。使用AQS可以简单且高效地构造出应用广泛的大量的同步器,比如SemaphoreFutureTask等皆是基于AQS的。

关关的博客 1478

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

最常用的锁:ReentrantLock可重入锁

本文系统解析了Java并发编程中的ReentrantLock同步工具。作为synchronized的增强替代方案,ReentrantLock通过AQS实现,具备可重入性、可中断锁获取、公平/非公平模式选择及多条件变量等核心特性。文章详细对比了其与synchronized的差异,包括灵活性、性能表现和使用复杂度,并给出标准使用流程(必须在try-finally中确保锁释放)和典型应用场景(如生产者-消费者模型)。最后指出应基于具体需求在易用性(synchronized)与高级功能(ReentrantLock)

2401_85973273的博客 1536

Java中的锁——队列同步器

队列同步器AbstractQueuedSynchronizer(AQS),似乎我们不经常用,但是它是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。下图显示了java.concurrent包的实现示意图 同步器的主要使用方式是继承,一般作为同步器组件的静态内部类,在同步器中仅定义了与状态相关的方法,且这个状

惟愿无事 8356

JUC并发编程 09——队列同步器AQS

JUC并发编程 09——队列同步器AQS

qq_62767608的博客 1159

ReentrantLock可重入锁又是怎么回事?

有关Synchronized锁的知识可以参考我上篇写的内容。

Java小白的博客 致力分享每一天学到的知识 1335

Java 独占锁与共享锁、公平锁与非公平锁、可重入锁

最近有一些小伙伴会问我一些关于并发相关的问题,在与他们的沟通中,我发现他们对锁的概念很模糊。这部分基础概念的缺失导致他们写的程序经常死锁,还无法bebug。虽然在网上有很多资料,但我还是用一些比较通俗易懂的例子,描述一下,帮助大家理解。

阿良的博客 3738

AQS核心窥探: 手写一个可重入锁,彻底理解CLH队列与State状态位

AQS是模板方法模式的典范:fill:#333;important;important;fill:none;fill:none;important;important;important;important;important;important;important;important;important;important;important;important;important;important;important;important;important;important;important;

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 1312

AQS---抽象队列同步器、CLH锁队列

自旋锁、CLH锁队列、AQS的锁队列,以ReentrantLock为例讲解AQS获取锁原理

qq_73181349的博客 1451

【Java并发编程之美 | 第三篇】Java 并发包中锁原理剖析之AQS、ReentrantLock独占可重入锁

Java 并发包中锁原理剖析之AQS、ReentrantLock独占可重入锁

踢桃的成长之路 1479

Java 并发编程归纳总结(可重入锁 | JMM | synchronized 实现原理)

可重入锁、Java 内存模型 JMM、synchronized 关键字实现原理

tmacfrank 1339

《JAVA并发编程的艺术》Java中的锁

《JAVA并发编程的艺术》Java中的锁 文章目录《JAVA并发编程的艺术》Java中的锁Lock接口代码清单5-1 LockUseCase.java表5-1 Lock接口提供的synchronized关键字不具备的主要特性表5-2 Lock的API队列同步器5.2.1 队列同步器的接口与示例表5-3 同步器可重写的方法表5-4 同步器提供的模板方法代码清单5-2 Mutex.java队列同步器的实现分析1. 同步队列表5-5 节点的属性类型与名称以及描述2. 独占式同步状态获取与释放代码清单5-3 同步器

weixin_44019182的博客 2247
上一篇: HTTP/2简介
下一篇: Java并发编程(五)—线程池ThreadPoolExecutor详解
Coding-ls
博客等级 码龄11年 41粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值