连载中 11/21

ReentrantLock:比 synchronized 多出来的四样东西

2026-10-28 · 32 阅读 · 0 评论 · 0 赞

什么时候轮到它出场

第 10 篇看过它的内脏,本篇说使用。先立选型观:默认场景用 synchronized——语法简洁、自动放锁、JVM 还在持续优化;只有需要下面四样能力之一时,才换 ReentrantLock。

能力一:可中断的等待

synchronized 等锁时对 interrupt 无感——线程挂在 BLOCKED 里纹丝不动,真死锁了只能重启进程。而 lockInterruptibly() 在等锁途中收到中断会抛 InterruptedException、立即退出排队。这是死锁恢复的正规解法之一:检测到死锁后,中断某个持锁线程打破僵局。

能力二:可超时的获取

tryLock() 无参版立刻返回,拿到拿不到都走人,不排队;带时间版等一会儿就撤。典型用法:抢锁失败就放弃或降级——记日志、走备用路径,「等不起」的后果由业务自己定义。缓存重建、防重复任务这类场景极好用:第二个线程发现有人在重建,转身就走,不傻等。

能力三:公平锁

构造时 new ReentrantLock(true) 开公平模式,严格按排队顺序发锁——呼应第 10 篇,差异只在要不要先看队列。但默认非公平是对的:公平锁吞吐明显更低,除非确认存在饿死且业务不可接受,否则别开。

能力四:多个条件队列 Condition

synchronized 只有一套等待队列,wait/notify 一锅粥,notify 还可能喊错人(第 4 篇的死锁案例)。ReentrantLock 可以 new 出多个 Condition,把不同目的的等待分组,signal 定点喊醒。用 Condition 重写第 4 篇的缓冲区:

class Buffer {
    private final Queue<Integer> data = new LinkedList<>();
    private final int cap = 10;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull  = lock.newCondition();   // 生产者的等待组
    private final Condition notEmpty = lock.newCondition();   // 消费者的等待组

    public void put(int v) throws InterruptedException {
        lock.lock();
        try {
            while (data.size() == cap) notFull.await();   // 满了,去生产者组等
            data.offer(v);
            notEmpty.signal();                             // 定点唤醒消费者
        } finally {
            lock.unlock();                                 // 必须 finally 释放
        }
    }
}

await 释放锁、signal 唤醒,语义对齐 wait/notify,但等待者分了组:notFull 上只挂生产者、notEmpty 上只挂消费者,signal 永不误伤。代价是纪律更严:await/signal 同样必须持锁调用;unlock 忘在 finally 里,异常一抛锁就永远还不回去——synchronized 自动放锁的省心,换成了这份自律要求。

最后提醒一句:ReentrantLock 的加解锁可以跨方法使用——灵活是双刃剑,lock 与 unlock 必须严格配对。选型检查清单:要可中断?要可超时?要公平?要多条件?四个都不要,回 synchronized。下一篇读写锁——读多写少的场景,独占太浪费了。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Java#ReentrantLock#Condition#JUC#并发编程

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10113 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9876 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9306 阅读 · 21 评论 · 287 赞