ReentrantLock 源码

【java】java ReentrantLock 源码详解 1.概述 案例:【Java】Java Controller 每次只能一个请求 多线程 ReentrantLock 详解参考:JUC锁: ReentrantLock详解 面经手册 · 第16篇《码农会锁,ReentrantLock之公平锁讲解和实现》 2.问题 什么是可重入,什么是可重入锁? 它用来解决什么问题? ReentrantLock的核心是AQS,那么它怎么来实现的,继承吗? 说说其类内部结构关系。 ReentrantLock是如何实现公平锁的? ReentrantLock是如何实现非公平锁的? . 阅读详情

ReentrantLock 的 lock 方法源码

ReentrantLock 中的 lock 方法,主要调用内部类 Sync 中的抽象 lock 方法。该方法主要有两套实现,一套是公平锁,一套是非公平锁。

公平锁

final void lock() {
    acquire(1);
}

公平锁中,直接调用 acquire 方法。

非公平锁

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

非公平锁中,首先尝试将 AQS 中的 state 属性从 0 变成 1,如果成功,则代表获取锁资源成功;否则调用 acquire 方法。

ReentrantLock 的 acquire 方法源码

acquire 方法中没有实际的业务处理,都是在调用其他方法。

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

首先调用 tryAcquire 方法,尝试获取锁资源,如果获取成功,则返回 true,方法结束。如果获取失败,则调用 && 后面的方法。
调用 addWaiter() 方法,将线程封装到 Node 节点并添加到队列尾部。
之后再调用 acquireQueued() 方法查看当前排队的 Node 是否在队列的前面,如果在前面,尝试获取锁资源。如果没在前面,线程进入到阻塞状态。

ReentrantLock 的 tryAcquire 方法源码

tryAcquire() 方法分公平锁和非公平锁两套实现,主要做了两件事:

如果 AQS 当前 state 为 0,尝试获取锁资源。
如果 AQS 当前 state 不为 0,查看是否是可重入操作。

公平锁

protected final boolean tryAcquire(int acquires) {
    // 获取当前线程
    final Thread current = Thread.currentThread();
    // 获取AQS当前state值
    int c = getState();
    // 判断state是否为0,为0则代表当前没有线程持有锁
    if (c == 0) {
        // 首先判断是否有线程在排队,如果有,tryAcquie()方法直接返回false
        // 如果没有,则尝试获取锁资源
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 如果state != 0,则代表有线程持有锁资源
    // 判断占有锁的线程是不是当前线程,如果是,则进行可重入操作
    else if (current == getExclusiveOwnerThread()) {
        // 可重入
        int nextc = c + acquires;
        // 检查锁重入是否超过最大值,二进制第一位表示符号 
        // 01111111 11111111 11111111 11111111
        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();
    // 获取AQS当前state值
    int c = getState();
    // 如果state == 0,说明没有线程占用着当前的锁资源
    if (c == 0) {
        // CAS直接尝试获取锁资源,直接抢锁,不管有没有线程在队列中
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        // 检查锁重入是否超过最大值,二进制第一位表示符号 
        // 01111111 11111111 11111111 11111111
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        // 修改state当前值
        setState(nextc);
        return true;
    }
    return false;
}

ReentrantLock 的 addWaiter 方法源码

private Node addWaiter(Node mode) {
    // 将当前线程封装为Node对象,mode为null,代表互斥锁
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    // pred指向tail
    Node pred = tail;
    if (pred != null) {
        // 当前线程Node节点的prev指向pred节点
        node.prev = pred;
        // 以CAS方式,尝试将node节点变成tail
        if (compareAndSetTail(pred, node)) {
            // 将pred的next指向node
            pred.next = node;
            return node;
        }
    }
    // 如果上述方式,CAS操作失败,导致加入到AQS末尾失败,就基于enq的方式添加到AQS队列
    enq(node);
    return node;
}

在 tryAcquire() 方法获取锁资源失败之后,首先创建当前线程的 Node 节点,之后将该节点添加到队列尾部。

private Node enq(final Node node) {
    // 死循环,直到插入成功
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            //如果尾节点为null,说明同步队列还未初始化,则CAS操作新建头节点
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 将node的prev指向当前的tail节点
            node.prev = t;
            // CAS尝试将node变成tail节点
            if (compareAndSetTail(t, node)) {
                // 将之前尾节点的next指向要插入的节点
                t.next = node;
                return t;
            }
        }
    }
}

ReentrantLock 的 acquirdQueued 方法源码

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 获取node的前一个节点
            final Node p = node.predecessor();
            // 如果前一个节点为head并尝试获取锁资源
            if (p == head && tryAcquire(arg)) {
                // 尝试获取锁资源成功,将node节点设置为头节点,thread和prev属性置位null
                setHead(node);
                // 将之前的头节点的next指向null,帮助快速GC
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 如果前一个节点不是head或者获取锁资源失败
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
// 确保上一个节点的状态是正确的
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        // 循环往前找,找到一个状态小于等于0的节点
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 如果不是-1,但是小于等于0,将状态修改为-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

acquireQueued 方法会查看当前排队的 Node 的前一个节点是不是 head,如果是,尝试获取锁资源。如果不是或者获取锁资源失败,那么就尝试将当前 Node 的线程挂起。
在挂起线程前,需要确认当前节点的上一个节点的状态是小于等于 0:
如果为 1,代表是取消的节点,不能挂起
如果为 -1,代表挂起当前线程
如果为 -2,-3,需要将状态改为 -1 之后,才能挂起当前线程

ReentrantLock 的 unlock 方法源码

public void unlock() {
    sync.release(1);
}
public final boolean release(int arg) {
    // 核心释放锁方法
    if (tryRelease(arg)) {
        Node h = head;
        // 如果头节点不为null,并且头节点的状态不为0,唤醒排队的线程
        if (h != null && h.waitStatus != 0)
            // 唤醒线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}
private void unparkSuccessor(Node node) {
    // 获取头节点状态
    int ws = node.waitStatus;
    // 如果头节点状态小于0,换为0
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    // 拿到当前节点的next
    Node s = node.next;
    // 如果s == null ,或者s的状态为1
    if (s == null || s.waitStatus > 0) {
        // next节点不需要唤醒,需要唤醒next的next
        s = null;
        // 从尾部往前找,找到状态正常的节点。(小于等于0代表正常状态)
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 经过循环的获取,如果拿到状态正常的节点,并且不为null
    if (s != null)
        // 线程唤醒
        LockSupport.unpark(s.thread);
}
// 核心的释放锁资源方法
protected final boolean tryRelease(int releases) {
    // state - 1
    int c = getState() - releases;
    // 如果释放锁的线程不是占用锁的线程,抛异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // 是否成功的将锁资源释放 
    boolean free = false;
    if (c == 0) {
        // 如果state = 0,代表成功释放锁资源
        free = true;
        setExclusiveOwnerThread(null);
    }
    // 设置state值
    setState(c);
    return free;
}
使用电荷放大器处理压电加速度计输出 电阻器 R p模拟传感器的绝缘电阻,为产生的电荷创建泄漏路径。在此图中,顶部波形显示传感器产生的电荷,而底部波形显示电荷放大器的输出。这需要具有大输入阻抗的放大级,以防止产生的电荷通过与传感元件并联的放大器的输入阻抗泄漏。换句话说,电荷放大器补偿传感器产生的电荷 q p ,反馈电容器 C F中具有相反极性的等量电荷。因此,任何与传感器并联的绝缘电阻,如电缆的绝缘电阻或传感器的漏电电阻Rp,都不能流过电流。漂移是指电荷放大器输出在一段时间内发生的变化,而不是由被测物理参数的变化(我们讨论的加速度)引起的。 阅读详情

相关推荐

跟着团子学SAP EPPM:项目管理常见的集成场景(PPM\PS\MS Project)

在SAP的EPPM解决方案中,针对企业战略级别的投资组合管理首先是定义合适的投资组合的层级结构,在投资组合下可以通过不同维度去定义投资篮(Portofolio Bucket)进行层层拆分,在最下层级的投资篮下可以分配明细的投资项(Portofolio Item),投资项可以是独立的一个一个具体执行的项目,也可以是单纯的一次服务等等。 今天主要聚焦在投资项的维度展开讲一下在SAP中投资项常见的几种集成模式: 集成场景1:使用PPM中的Project Management进行项目管理 第一个场景是在创建投资项

Lucas1211的博客 4141

ReentrantLock源码解析

解析了ReentrantLock类的部分重点源码。

weixin_48288539的博客 970

Unidbg逆向阿里系App签名:从环境补全到算法还原实战

在移动安全与协议分析领域,理解应用层签名机制是核心挑战之一。签名算法作为保障通信安全与身份验证的关键技术,其原理通常涉及哈希、加密及密钥协商等基础密码学概念。在Android平台,核心签名逻辑常被加固并隐藏于Native层SO库中,以对抗动态调试与静态分析,这为安全研究和协议兼容带来了技术障碍。Unidbg作为一种基于模拟执行的逆向工程工具,通过模拟ARM指令集和系统调用,允许研究者在脱离真机环境的情况下,可控地执行和调试SO文件,为解决此类问题提供了创新思路。其技术价值在于实现了对加固代码的确定性分析与算

weixin_42525738的博客 940

并发编程基础ReentrantLock源码分析

//Note1:(双向链表。初始的时候head和tail都指向Null,之后添加新节点的时候会创建一个空Node,head和tail都指向这个空Node代表初始化完成。尾插法入队通过移动尾结点tail往前移动,出队通过移动head节点head节点往前移动。当Head节点和tail指向同一个Node时代表没有阻塞队列中没有等待的线程) //之后唤醒的时候也会通过tail尾结点一直像后遍历拿到真正处于等待状态的Node线程,然后通过LockSupport提供的Unpark指定唤醒Node线程; // 阻塞的时候

北洋的博客 3133

解决VMWare ESXi PCIe直通显卡导致的DevicePowerOn启动故障

本文详细解析了VMWare ESXi中PCIe直通显卡导致的DevicePowerOn启动故障,重点分析了MMIO空间不足的根本原因,并提供了完整的解决方案。通过修改.vmx文件中的关键参数,如pciPassthru.64bitMMIOSizeGB,可以有效解决显存分配问题。文章还涵盖了硬件兼容性检查、性能调优技巧及大规模部署的实战案例,帮助用户彻底解决显卡直通难题。

weixin_42537919的博客 577

STC8G1K08A 实战应用:从配置到功能实现

本文详细介绍了STC8G1K08A单片机的实战应用,从开发环境搭建、GPIO配置、ADC模块使用到定时器中断处理和PWM输出等核心功能。重点讲解了外设模块的协同工作配置技巧,并分享了实际项目开发中的经验教训,帮助开发者快速掌握STC8G1K08A的高效应用。

n4o5p6q7r的博客 392

并发编程源码解析(一)ReentrantLock源码解析(超详细)

是中的一个互斥锁,它允许线程重入锁定的代码段,而不会产生死锁。它实现了Lock 接口,内部继承了(AbstractQueuedSynchronizer) 并提供了比更多的灵活性和功能,例如可中断锁、超时锁、公平锁和多条件变量。的主要作用是实现线程间的同步和互斥访问共享资源,保证数据的一致性和完整性。在锁竞争的比较激烈的情况,优先推荐使用,具体原因以后解析的时候再来详细说明。

qq_33781946的博客 946

37、基于视觉的轻量级面部呼吸和心率测量技术

本文提出了一种基于视觉的轻量级面部呼吸和心率测量技术,通过远程光电容积脉搏波描记法(RPPG)和面部振动信息实现非接触式生理信号采集。创新性地结合框选和特征点选择多个感兴趣区域(ROI),并采用光流跟踪、主成分分析(PCA)和集成经验模态分解(EEMD)等方法优化信号处理流程。实验结果表明,该方法在测量精度和稳定性方面优于传统方法,并可在树莓派4等低功耗平台上实现实时运行,具有广泛的应用前景。

r7s8t的博客 307

ReentrantLock的超详细源码解析

ReentrantLock是基于AQS框架的一个子类,实现了可重入锁,其中有公平与非公平两种类型,是一个非常重要的工具,也是学习鬼斧神工一般的AQS的很好的出发点注:本文适合想要深究AQS的队列的实现和原理,ReentrantLock的机制的人参考,文章较长其实还是比较好理解的,只要理解了基本框架,接下来的超时锁,可打断锁,条件队列这些就不足为惧了本质上就是把普通的可重入锁加了个超时结束,遇到打断直接抛出异常。

qq_30004513的博客 1424

PD3.0详解 第六章 协议层—数据消息之—VDM

PD3.0的VDM 很多对PD3.0协议了解不是很深入的同学可能对VDM有一些陌生,因为这个VDM是较为次要,只不过相对沟通电源的协议来讲VDM是次要,但从PD3.0协议的角度来说,这个也是非常重要的,是必须要非常明白的了解这个东西,所以今天大师匈就分享一下这部分的重要知识点。 VDM (vendor define message) VDM是什么意思呢,就是原厂定义的消息,比如PD快充芯片的原厂定义的一些自己独立的message。但是呢这部分也有规定的一些标准的字段。 另外我们要很清楚PD协议的数据分为几类

大师匈的博客 1万+

十三种通信接口芯片——《器件手册--通信接口芯片》

通信接口芯片用于实现差分信号的传输,具有抗干扰能力强、传输距离远等优点。RS485支持多点通信,可连接多个收发器,而RS422支持单向多点通信。广泛应用于工业自动化、过程控制、仪器仪表、楼宇自动化(如暖通空调、保安系统)、电机控制等领域。ST3485EBDRTP8485ESR用于实现串行通信,支持点对点通信,传输距离相对较短,数据速率较低。常用于计算机与外设(如打印机、调制解调器)之间的通信。如芯力特的SIT3232,可作为ST3232的替代品。

XU157303764的博客 4276

ReentrantLock源码详解

ReentrantLock源码看了很多遍,也总是忘,这次把源码逻辑仔仔细细的写一遍,为了供以后复习,也给各位正在学习的同学们一个参考。相信大家都熟悉,ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。

qq_41346335的博客 1158

5.1 rocr-runtime libhsakmt event 机制全景—从 Doorbell 到 Signal

本文介绍了ROCm/KFD中的Event机制架构,主要解决GPU异步计算中的反馈问题。Event机制分为三层:硬件层触发事件,内核层管理事件通知,用户态接收处理。事件类型分为三大类:同步类事件(如SIGNAL、QUEUE_EVENT)用于任务完成通知,异常类事件(如MEMORY、HW_EXCEPTION)报告错误,拓扑类事件(如NODECHANGE)处理设备变更。该机制通过共享内存和中断实现高效通信,避免CPU轮询开销。后续文章将深入内核实现、Thunk层封装和多队列完成检测实战。

图形/计算GPU的驱动与虚拟化技术分享园地 1308

【源码系列】ReentrantLock原理

呼~ 终于把这个源码总结完毕,实际上总结一遍后还是很模糊,唯一的办法就是理解+重复,希望大家可以多理解理解,这个代码很复杂,但是啃下来还是获益良多的 ~抽空再补充一下流程图,这样会好理解很多 ~

Y1zH1yuan的博客 1485

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

ReentrantLock源码解读

在没接触java.util.concurrent并发包之前,大家在多线程情况下使用到的都是synchronized同步锁,这是c++也就是jvm层面到的锁,不方便使用者观看源码,也不能对其扩展。所以今天给大家来解读一下ReentrantLock锁的源码,自带手绘图从图上理解AQS内部的构造,从图上理解AQS内部队列的初始化和添加节点过程,从多个角度考虑公平锁和非公平锁。传授看各种源码的思路和经验。

李哈zzz的博客 1046

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

ReentrantLock 源码解析

ReentrantLock 实现了 Lock 接口,内部基于 AQS 实现。所以想要弄懂 ReentrantLock ,一定要先弄明白AQS。ReentrantLock 是可重入锁,同一个线程能够多次获取到同一个 ReentrantLock 锁。ReentrantLock 类采用的是 AQS 技术中的独占模式,实现了 AQS 的 3 个关键方法:tryAcquire(),tryRelease(),isHeldExclusively()方法。

weixin_45278293的博客 1541

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

ReentrantLock源码研读

继续整理这几天读的源码 ReentrantLock是AQS的一个实现,让我们看看它的源码 其有三个内部类Sync,NonfairSync,FairSync,后面两个继承了Sync,Sync继承了AQS。后面二者是真正工作的类,NonfairSync用于非公平锁,FairSync用于公平锁,这二者除了tryAcquire方法外都使用Sync的方法实现,我们先看看Sync 首先是这个nonfairTryAcquire,逻辑很简单,如果当前state是0即锁空闲,则CAS加锁,成功后使用setExclu

qq_16899143的博客 140

ReentrantLock源码阅读

ReentrantLock 是jdk提供的一种锁机制,该锁支持公平锁和非公平锁这两种。这两种锁的实现原理是 通过一 锁队列,公平锁是按照锁队列顺序执行,而非公平锁则不是按照锁队列顺序执行,会存在锁竞争,因为新锁会和锁队列中等待的锁进行竞争,所以有可能新锁会竞争到锁(如果竞争不到则加入锁队列)。下图为类依赖关系。 NofairSync , FairSync 是 ReentrantLock 中的内部类。分别是非公平和公平锁。 接下来从源码入手,先查看NofairSync: new ReentrantLock()

qq_29499107的博客 1万+

jdk11源码-ReentrantLock源码

概述 ReentrantLock是java中常用的加锁工具,下面是一个典型的写法: ReentrantLock lock = null; try { System.out.println(System.currentTimeMillis()); lock = new ReentrantLock(); lock.lock(); lock.lock(); Ti...

刘本龙的专栏 1779

ReentrantLock源码解析——一篇文章搞定ReentrantLock

一.概述 想要看懂ReentrantLock源码必须知道两个知识点 1.CAS,既比较并交换,是解决多线程并发情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作——内存位置(V),预期原值(A)和新值。进行操作时,如果内存位置的值和预期原值一样,则更新为新值,否则不做操作。 2.AbstractQueuedSynchronizer简称AQS:AQS是一个FIFO等待队列的阻塞锁和相关...

rongshisuo的博客 467

ReentrantLock源码分析

for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))

林伟杰的博客 1022

05.ReentrantLock源码解析

ReentrantLock源码解析 ReentrantLock类图 加锁成功流程分析 1.构造器分析 public ReentrantLock() { //默认为非公平锁NonfairSync继承自Sync又继承自AQS。 sync = new NonfairSync(); } 2.lock()方法 final void lock() { //使用CAS去修改state如果修改成功则说明获取到了锁则然后设置独占状态下这把锁的主人是当前线程。 if (compareAnd

weixin_43891622的博客 233

ReentrantLock源码实现

ReentrantLock源码实现 一、示例使用 示例 public class ReentrantLockDemo { public static void main(String[] args) throws Exception { Service service = new Service(); Thread t01 = new Thread((...

瘦子没有夏天 775

ReentrantLock源码探究

ReentrantLock源码探究 后面的流程可以结合:https://www.cnblogs.com/dalianpai/p/14202617.html 一起看 相对于 synchronized 它具备如下特点 可中断 可以设置超时时间 可以设置为公平锁 支持多个条件变量 与 synchronized 一样,都支持可重入 可重入 可重入是指同一个线程如果首次获得了这把锁,那么因为它...

qq_29860591的博客 336
上一篇: 阿里P8架构师“墙裂”推荐,JVM性能调优看这篇就够了
下一篇: Project Reactor源码阅读-Scheduler
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值