Java ReentrantLock 冗余设计? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
HowieWang
V2EX    Java

Java ReentrantLock 冗余设计?

  •  1
     
  •   HowieWang 265 天前 3228 次点击
    这是一个创建于 265 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景

    偶然看到了一篇 ReentrantLock 的源码分析文章,自己便去学习了一下源码。 核心思想是如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。

    问题

    ReentrantLock 的 lock 方法在入队之前直接获取锁,是否冗余设计,存在冗余的代码执行?

    h1>我的观点

    进行了冗余设计,理解如下。

    jdk8

    lock 简化代码如下,我的观点是:非公平锁(NofairSync) lock 方法,有两次尝试直接获取锁,是否冗余设计了?

    static final class NonfairSync extends Sync { final void lock() { // 直接尝试获取锁,不公平表现 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); // acquire 方法中首先会调用 tryAcquire ,会第二次尝试获取锁 } protected final boolean tryAcquire(int acquires) { // 如果资源空闲,直接尝试获取锁 // 如果资源不空闲,判断是否当前线程占有,进行重入锁 } } static final class FairSync extends Sync { final void lock() { acquire(1); } protected final boolean tryAcquire(int acquires) { // 如果资源空闲,判断是否已经入队,进行公平处理 // 如果资源不空闲,判断是否当前线程占有,进行重入锁 } } class AbstractQueuedSynchronizer { public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } } 

    jdk17

    lock 简化代码如下,我的观点是:lock 方法,有两次尝试直接获取锁,是否冗余设计了?对于公平锁,均会判断当前线程是否已经入队,是冗余的尝试;对于非公平锁,连续两次尝试直接获取锁,也是冗余的尝试

    abstract static class Sync extends AbstractQueuedSynchronizer { abstract boolean initialTryLock(); final void lock() { if (!initialTryLock()) // 首次尝试获取锁 acquire(1); // acquire 方法中首先会调用 tryAcquire ,会第二次尝试获取锁 } } static final class NonfairSync extends Sync { final boolean initialTryLock() { // 如果资源空闲,直接尝试获取锁 // 如果资源不空闲,判断是否当前线程占有,进行重入锁 } protected final boolean tryAcquire(int acquires) { // 如果资源空闲,直接尝试获取锁 } } static final class FairSync extends Sync { final boolean initialTryLock() { // 如果资源空闲,判断是否已经入队,进行公平处理 // 如果资源不空闲,判断是否当前线程占有,进行重入锁 } protected final boolean tryAcquire(int acquires) { // 如果资源空闲,判断是否已经入队,进行公平处理 } } 

    我认为的不冗余的方式

    static class Sync extends AbstractQueuedSynchronizer { final void lock() { acquire(1); } } static final class NonfairSync extends Sync { protected final boolean tryAcquire(int acquires) { // 如果资源空闲,直接尝试获取锁 // 如果资源不空闲,判断是否当前线程占有,进行重入锁 } } static final class FairSync extends Sync { protected final boolean tryAcquire(int acquires) { // 如果资源空闲,判断是否已经入队,进行公平处理 // 如果资源不空闲,判断是否当前线程占有,进行重入锁 } } 
    7 条回复    2025-03-11 00:05:19 +08:00
    herm2s
        1
    herm2s  
       265 天前   2
    不算设计冗余,两次调用 compareAndSetState 都是为了在无竞争时快速成功。第一次调用返回 false 意味着有竞争,此时进入 acquire 。在 acquire 里会根据 state 的值判断是否调用 compareAndSetState ,原因是这中间有可能从有竞争变为无竞争( volatile ),此时快速成功就行了。state 的值>0 才是可重入锁/其它线程占用的情况,最后再做其它处理。
    Rickkkkkkk
        2
    Rickkkkkkk  
       265 天前
    你的疑问算是一个面试题了
    kandaakihito
        3
    kandaakihito  
       265 天前
    经典面试题。跟一楼说的一样,大部分时候资源是不上锁的,一般能快速拿到锁就拉倒了没必要再去 AQS 里面拐一圈。
    这也算是并发编程里面的“快慢路径”的一种体现。
    mamumu
        4
    mamumu  
       265 天前
    学到了
    JoJoWuBeHumble
        5
    JoJoWuBeHumble  
       265 天前
    div class="reply_content">其实把你的问题直接问 AI ,都有答案了。
    1. 非公平锁的“两次尝试”设计
    在非公平锁中,lock() 方法在入队前先尝试直接获取锁(通过 initialTryLock() 或 CAS 操作),若失败再调用 acquire(),而在 acquire() 中又会调用 tryAcquire() 再次尝试。这看似重复,实为优化:

    减少线程挂起/唤醒的开销:当锁被释放时,新线程可能直接抢占锁(插队),而无需等待队列中的线程被唤醒,提高了吞吐量。
    避免不必要的队列操作:若首次尝试成功,线程直接获取锁,无需进入队列,减少了入队、出队和上下文切换的开销。
    gitrebase
        6
    gitrebase  
       265 天前
    并发编程的经典优化方式:快慢路径

    一般先走 Fast Path ,用 CAS 尝试快速获取资源;失败后,走 Slow Path ,可能会涉及到一些阻塞、沉睡、排队之类的操作
    billccn
        7
    billccn  
       265 天前   3
    我觉得要重点提出 1 楼说的“这中间有可能从有竞争变为无竞争( volatile )”,我觉了楼主可能犯了并发编程里一个常见是思维错误就是我“刚刚” 判断了一个条件,后面就不用判断了。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3976 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 04:11 PVG 12:11 LAX 20:11 JFK 23:11
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86