ReentrantLock解析

Keysight ADS新手必看:3分钟搞定TDR模拟的保姆级教程(附差分信号设置技巧) 本文为Keysight ADS新手提供TDR模拟的保姆级教程,详细介绍了从基础环境准备到差分信号设置的全流程。通过内置模板调用、待测器件连接和仿真结果解读,帮助用户快速掌握TDR模拟技术,特别包含差分信号配置技巧,提升信号完整性分析效率。 阅读详情

转自:http://blog.youkuaiyun.com/yanlinwang/article/details/40450769

最近学习Jdk的源代码时,读到了ConcurrentHashMap的源码实现时,发现每一个分段Segment都是ReentrantLock类型,于是顺带对ReentrantLock的源代码进行了学习。。在这里做一个笔记总结。因为只有在工作之余才能有空看看,所以思路有点零散,仅供参考。。。。

1、如何确定哪个线程可重复进入该锁
     
          在获取锁的时候,首先会检查当前同步对象的阻塞状态,如果已经是被某个线程持有,会检查持有的线程是否就是当前线程。同步对象有一个 exclusiveOwnerThread 属性用来表征占有此同步对象的线程。

      如果当前线程就是持有该同步对象的线程,那么就不用阻塞。

     具体逻辑通过下面代码可以表明(FairSync类),可以与 nonfairTryAcquire来对比去理解公平锁和非公平锁的含义

    公平锁:公平锁讲究先来先到,线程在获取锁时,如果这个锁的等待队列中已经有线程在等待,那么当前线程就会进入等待队列中

     非公平锁:不管是否有等待队列,如果可以获取锁,则立刻占有锁对象。

       protected  final  boolean  tryAcquire( int  acquires) {
             final  Thread current = Thread.currentThread();
             int  c = getState();
             if  (c == 0) {
                 if  (isFirst(current) &&
                    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  ;
        }
    }

2、同一线程多次进入临界区
     通过检查拥有同步对象是否为当前线程来确认是否可以运行当前线程进入临界区。同步对象有一个state字段来表明,拥有此同步对象的线程,进入临界区的次数。

3、什么时候临界区是没有加锁状态
     同步对象的state字段为0,则表示当前没有任何线程拥有此同步对象。
     

4、实现的原理和逻辑

     内部逻辑通过Sync对象来实现加锁和解锁,重点关注AbstractQueuedSynchronizer抽象类的实现。

     使用一个非循环的双向链表(FIFO)来维护等待线程队列,一个线程在进行状态获取的时候,如果获取不能马上获取成功,就会加入到这个队列的队尾中。这个队列中的线程能够运行的条件如“队列访问控制管理”的描述

     AbstractQueuedSynchronizer的等待(sync)队列访问控制管理:只有一个线程能够在同一时刻运行,其他的进入等待状态。每个线程都是一个独立的个体,它们自省地观察,当自己的前驱节点是头节点并且已经原子性地获取了状态,这个线程才能运行。

注意对锁的理解:1、锁自身的状态(加锁的次数、当前拥有锁的线程);2、等待在该锁上的等待线程队列。


 线程进入sync队列之后,接下来就是要进行锁的获取,或者说是访问控制了,只有一个线程能够在同一时刻继续的运行,而其他的进入等待状态。而每个线程都是一 个独立的个体,它们自省的观察,当条件满足的时候(自己的前驱是头结点并且原子性的获取了状态),那么这个线程能够继续运行。

AbstractQueuedSynchronizer维护的队列中线程状态比较有意思:SIGNAL是表明当前节点的下一个节点需要unparking(当成解锁来理解),另外两个状态(CANCELLED和CONDITION则都是表示自身的状态)

AbstractQueuedSynchronizer的解析:

    state,当前的同步状态,0表示未加锁,非0表示已加锁,同时对于ReentrantLock来说,这个值表示同一个进程加锁的次数
    
     AQS同步器的核心主要是acquireQueued和shouldParkAfterFailedAcquire

acquireQueued用来处理AbstractQueuedSynchronizer的等待队列,同时检查某一个节点是否能够进入临界区。代码如下

  final  boolean  acquireQueued( final  Node node,  int  arg) {
         try  {
             boolean  interrupted =  false ;
             for  (;;) {
                 final  Node p = node.predecessor();
                 if  (p ==  head  && tryAcquire(arg)) {
                    setHead(node);
                    p.  next  =  null ;  // help GC
                     return  interrupted;
                }
                 if  (shouldParkAfterFailedAcquire (p, node) &&
                    parkAndCheckInterrupt())
                    interrupted =  true ;
            }
        }  catch  (RuntimeException ex) {
            cancelAcquire(node);
             throw  ex;
        }
    }



final  Node p = node.predecessor();和 if  (p ==  head  && tryAcquire(arg)) 用来检查要检查的节点是否满足进入临界区的条件:当前节点是等待队列中的第一个节点(前驱节点是头节点p==head),同时能够成功加锁(tryAcquire返回true)。如果满足状态,就移动头指针。

如果要检查的节点不满足加锁条件,那就执行到 shouldParkAfterFailedAcquire  方法来讲线程进行阻塞
private  static  boolean  shouldParkAfterFailedAcquire(Node pred, Node node) {
         int  ws = pred. waitStatus  ;
         if  (ws == Node. SIGNAL )
             /*
             * This node has already set status asking a release
             * to signal it, so it can safely park
             */
             return  true  ;
         if  (ws > 0) {
             /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do  {
              node.  prev  = pred = pred.  prev ;
           }  while  (pred. waitStatus  > 0);
           pred.  next  = node;
        }  else  {
             /*
             * waitStatus must be 0 or PROPAGATE. Indicate that we
             * need a signal, but don't park yet. Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node. SIGNAL );
        }
         return  false  ;
    }
shouldParkAfterFailedAcquire方法用来检查线程是否满足阻塞的条件,同时会清理掉队列中一些已经过期的节点(已取消,节点的waitStatus大于0),检查原则:

  • 规则1:如果前继的节点状态为SIGNAL,表明当前节点需要unpark,则返回成功,此时acquireQueued方法的第12行(parkAndCheckInterrupt)将导致线程阻塞
  • 规则2:如果前继节点状态为CANCELLED(ws>0),说明前置节点已经被放弃,则回溯到一个非取消的前继节点,返回false,acquireQueued方法的无限循环将递归调用该方法,直至规则1返回true,导致线程阻塞
  • 规则3:如果前继节点状态为非SIGNAL、非CANCELLED,则设置前继的状态为SIGNAL,返回false后进入acquireQueued的无限循环,与规则2同
注意对 shouldParkAfterFailedAcquire的调用是在 acquireQueued的一个无限循环中调用的,但这个循环最终肯定是会有出口的,就在于 shouldParkAfterFailedAcquire会修改前驱节点的状态,最后会使得方法调用到parkAndCheckInterrupt中,完成线程的阻塞。即便最终队列中就只剩下头节点(只是作为头节点标记)和当前节点,无限循环也是有出口,因为初始构造的头节点的waitStatus是为0的。所以最后,会把头节点的waitStatus设置为Node.SIGNAL,这样就会导致对当前节点调用parkAndCheckInterrupt方法。

完成线程阻塞:
对线程完成阻塞是在parkAndCheckInterrupt方法中调用的,通过LockSupport类来实现的。
设置当前线程的阻塞对象(每个线程有一个parkBlocker属性),然后通过系统调用实现线程的阻塞。线程恢复运行后,再把线程的阻塞对象设置为null。

下面是基本的加锁流程图


5、锁的释放

在加锁的时候会涉及到线程的阻塞,因此在当前释放锁对象的时候,会涉及到等待队列上线程的唤醒。

锁的释放,其实涉及两步:1、设置锁对象本身的状态(state),也就是当前锁的加锁次数;2、在这个锁对象上等待队列中线程的唤醒(这个只有在锁对象完全被释放以后才会进行)。

设置锁对象本身的状态,由各个子对象自己完成。当然在释放锁的时候,会检查这个线程是否就是当前拥有此锁对象的线程。

下面是ReentrantLock内部锁对象的实现,同时tryRelease的返回会指示是否需要进行等待队列的唤醒。
protected   final  boolean  tryRelease ( int  releases) {
             int  c = getState() - releases;
             if  (Thread.currentThread() != getExclusiveOwnerThread())
                 throw   new  IllegalMonitorStateException();
             boolean  free =  false  ;
             if  (c == 0) {
                free =  true  ;
                setExclusiveOwnerThread(  null  );
            }
            setState(c);
             return  free;
        }

如果tryRelease返回true,那就表明当前线程已经完全释放锁了,这个时候就需要从等待队列中唤醒线程。

public   final  boolean  release ( int  arg) {
         if  (tryRelease(arg)) {
            Node h =  head ;
             if  (h !=  null  && h.  waitStatus  != 0)
                unparkSuccessor(h);
             return   true  ;
        }
         return   false  ;
    }

如果等待队列存在,那就唤醒第一个满足条件的线程,unparkSuccessor中传入的是头节点。
   private   void  unparkSuccessor(Node node) {
         /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling. It is OK if this
         * fails or if status is changed by waiting thread.
         */
         int  ws = node. waitStatus  ;
         if  (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

         /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.  next ; 
         if  (s ==  null  || s. waitStatus  > 0) {
            s =  null  ;
             for  (Node t =  tail  ; t !=  null  && t != node; t = t.  prev )
                 if  (t. waitStatus  <= 0)
                    s = t;
        }
         if  (s !=  null  )
            LockSupport. unpark(s.  thread );
    }

注意这里的Node s=node.next;这里唤醒的是等待队列中的第一个节点(阻塞状态),也就是前驱节点是头节点的那个节点,这样的唤醒顺序,也正是与进入等待队列的顺序一致。
IPD流程中CBB管理的5个致命误区,第3个坑了80%的团队 本文深入剖析了IPD流程中CBB管理的五个致命误区,尤其指出缺乏有效的度量与激励机制是影响80%团队的核心陷阱。文章强调CBB的核心在于前瞻性设计,而非简单归档,并需平衡通用性与产品竞争力,同时建立持续运营机制,确保CBB与市场需求对齐,从而真正实现缩短开发周期、降低成本的目标。 阅读详情

相关推荐

基于Python适用于YOLO目标检测算法的图片数据增强处理

为了增强模型的鲁棒性以及数据的丰富性,同 时为了防止由于数据过少发生过拟合,本设计对数 据集进行数据增强处理,包括随机亮度调节、随机 对比度调节、随机饱和度调节、加入高斯噪声、水 平翻转、运动模糊和旋转 15°

weixin_67136409的博客 700

ReentrantLock详解

ReentrantLock使用,

weixin_56738054的博客 1万+

模拟CMOS 第2章 MOS器件物理基础

模拟CMOS 第2章 MOS器件物理基础 文章目录模拟CMOS 第2章 MOS器件物理基础2.1 基本概念2.1.1 MOSEFT开关2.1.2 MOSEFT结构2.1.3 MOS符号2.2 MOS的I/V特性2.2.1 阈值电压2.2.2 I/V特性的推导2.3 二级效应2.4 MOS器件模型2.4.1 MOS器件版图2.4.2 MOS器件电容2.4.3 MOS小信号模型2.4.4 MOS SPICE模型2.4.5 NMOS与PMOS器件的比较2.4.6 长沟道器件与短沟道器件的比较附录A 用作电容器MO

椰子奶糖的博客 5206

ReentrantLock 锁详解

ReentrantLock 支持公平锁和非公平锁,可重入锁 ReentrantLock的底层是通过 **AQS[链接]**实现。 一、BAT 大厂的面试题 **【1】**什么是可重入,什么是可重入锁? 它用来解决什么问题? 【2】ReentrantLock 的核心是 AQS,那么它怎么来实现的,继承吗? 说说其类内部结构关系。 【3】ReentrantLock 是如何实现公平锁的? 【4】ReentrantLock 是如何实现非公平锁的? 【5】ReentrantLock 默认实现的是公平还是非公平锁.

m0_37235955的博客 3692

大疆无人机图像传输延时优化全解析:从Lightbridge到OcuSync的实战技巧

本文深入解析大疆无人机图像传输延时优化,对比Lightbridge与OcuSync两大核心通信协议的技术差异与实战表现。文章详细拆解了从传感器采集到屏幕显示的完整延时链条,并提供了从环境频段选择、硬件升级到软件设置的全方位优化技巧,帮助飞手与开发者显著降低图传延时,提升操控体验与作业效率。

weixin_29163857的博客 306

全国区块链职业技能大赛国赛考题前端功能开发

全国区块链职业技能大赛国赛考题区块链应用前端功能开发

本郡主是喵 1335

【面试必问系列】之ReentrantLock详解

的队列,可以用于构建锁的基础框架,内部通过原子变量state来表示锁的状态,当state大于0的时候表示锁被占用,如果state等于0时表示没有占用锁,ReentrantLock是一个重入锁,表现在state上,如果持有锁的线程重复获取锁时,它会将state状态进行递增,也就是获得一个信号量,当释放锁时,同时也是释放了信号量,信号量跟随减少,如果上一个线程还没有完成任务,则会进行入队等待操作。而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。

一个喜欢诗和远方的程序媛 8292

深入浅出块I/O子系统(四)请求处理过程

BIO请求处理的流程 函数scsi_request_fn()代码(摘自文件drivers/scsi/scsi_lib.c) (1)static void scsi_request_fn(struct request_queue *q) (2)int scsi_dispatch_cmd(struct scsi_cmnd *cmd) { host->hostt->queuecommand(host, cmd) } (3)struct sas_internal *i sas_queuec

sinat_37817094的博客 1348

ReentrantLock锁

1. 简介 ReentrantLock,可重入锁,是一种递归无阻塞的同步机制。它可以等同于 synchronized 的使用,但是 ReentrantLock 提供了比 synchronized 更强大、灵活的锁机制,可以减少死锁发生的概率

weixin_46121031的博客 7642

医学图像处理实战:利用OpenSlide解析病理SVS切片的完整解决方案

本文介绍了使用openslide处理病理SVS切片的完整流程,从基础安装到科研应用。主要内容包括:1)SVS切片特点及openslide工具简介;2)环境配置与软件安装;3)基础操作:读取切片、查看信息、可视化不同层级图像;4)进阶操作:坐标裁剪与批量处理;5)科研方向拓展:结合AI模型进行病理分割分类、量化分析及可视化报告。教程提供了完整的代码示例,帮助毕业设计学生快速掌握病理图像分析的核心技能。

m0_71589036的博客 313

VS2022编译xlnt库

xlnt是一个用于读写Excel文件的C++库,本文介绍如何用源代码编译静态或动态xlnt库

QQVQQ的博客 7288

ReentrantLock 基本原理

ReentrantLock底层基于AQS,其构造方法返回的就是NonfaireSync和faireSync;两种同步器都继承自Sync,Sync又继承自AQS!newReentrantLock时,返回的就是同步器,默认是非公平的;

机械工程跑路哥 3616

mabl:AI原生测试自动化平台的基础使用

mabl是一个基于人工智能和机器学习的云原生测试自动化平台。它通过使用先进的技术,如机器学习,自动创建和执行测试,自动分析测试结果,并根据需求的变化自动适配和维护测试。这样的自动化过程极大地提高了软件测试的效率和准确性。mabl的口号是 “You write code, mabl tests”,意味着开发者只需关注编写代码,而测试工作则可以交给mabl来完成。mabl的平台支持多种测试类型,包括 Web、App、Api、可访问性和性能测试,使得团队能够更快速、更有信心地发布软件。

blues_C的博客 3661

ReentrantLock源码解析

ReentrantLock源码解析

sheepherder 579

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

ReentrantLock的超详细源码解析

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

qq_30004513的博客 1424

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

ReentrantLock 源码解析

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

weixin_45278293的博客 1541

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

ReentrantLock核心解析

ReentrantLock是Java并发包()提供的可重入锁,相比,它提供了更灵活的锁控制机制,如公平锁/非公平锁、条件变量(Condition)、可中断锁、超时锁等。可重入是指同一个线程可以多次获取同一把锁,不会导致死锁。例如: 重入计数:ReentrantLock内部维护一个计数器,线程每获取一次锁,计数器+1;释放一次,计数器-1;计数器为0时,锁真正释放。 二、公平锁 vs 非公平锁:锁的获取策略 1. 核心区别 类型 策略 优点 缺点 适用场景 公平锁 按请求顺序获取锁(先到先

weixin_43076660的博客 911

ReentrantLock源码+图深度解析呀

彻底理解ReentrantLock简介源码解析   java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。这篇文章主要是从使用的角度来分析一下ReentrantLock。 简介   ReentrantLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。 synchronized是独占锁,加锁和解锁的过程自动

可能是个最差的程序员了 280

【源码解析】ReentrantLock

【源码解析】ReentrantLock

人类所有真实的快乐,一定是恒久的努力 1072
上一篇: 深入浅出Java并发包—锁机制(一)
下一篇: ThreadLocal变量存储的实际原理与线程安全原因
wingahi
博客等级 码龄15年 15粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值