李锋镝的博客

  • 首页
  • 时间轴
  • 说说
  • 读书观影
  • 系列
  • AI工具集
  • 论坛
  • 友圈
    • 友链
    • 朋友圈
    • 游客中心
  • 留言
    • 留言
    • 走心评论
  • 关于
    • 关于本站
    • Now
    • 每日心情
    • 网站地图
    • 网站统计
    • 赞助
  • 星球穿梭
  • 🚇开往
静亦定 动亦定
惟坚韧者始能遂其志
  1. 首页
  2. 原创
  3. 技术
  4. 正文

ReentrantLock深度解析

2025年5月30日 219 0 0
本文最后更新于 2025年5月30日,距今已 526 天,其中的信息可能已经发生变化,请注意甄别。

一、核心设计思想

1. 可重入性(Reentrancy)

定义:同一线程可多次获取同一把锁,通过计数器记录持有次数,释放时需完全解锁(计数器归零)。
示例代码:

ReentrantLock lock = new ReentrantLock();  
lock.lock();  
try {  
    lock.lock(); // 可重入,计数器+1  
    // 临界区代码  
} finally {  
    lock.unlock(); // 计数器-1  
    lock.unlock(); // 计数器归零,释放锁  
}  

2. 公平性(Fairness)

  • 公平锁:按请求顺序(FIFO)分配锁,避免线程饥饿,适合高竞争场景。
  • 非公平锁(默认):允许线程插队,减少上下文切换开销,适合低竞争场景,吞吐量更高。

3. 显式锁控制

  • 手动加锁/解锁:需通过try-finally确保锁释放,避免死锁。
  • 灵活性:支持tryLock(尝试获取)、lockInterruptibly(可中断获取)、超时获取等。

二、底层实现:AQS(AbstractQueuedSynchronizer)

ReentrantLock基于AQS框架实现,通过CLH双向队列管理等待线程,核心依赖state状态变量和节点(Node)机制。

1. AQS核心机制

  • state变量:表示锁的持有计数(0为未锁定,≥1为锁定次数)。
  • CLH队列:保存等待线程的双向链表,每个节点封装线程状态(如CANCELLED、SIGNAL)。

2. 加锁流程(非公平锁为例)

file

3. 解锁流程

public void unlock() {  
    sync.release(1); // 调用AQS的release方法  
}  

// AQS的release方法  
public final boolean release(int arg) {  
    if (tryRelease(arg)) { // 尝试释放锁(ReentrantLock实现)  
        Node h = head;  
        if (h != null && h.waitStatus != 0)  
            unparkSuccessor(h); // 唤醒队列中下一个线程  
        return true;  
    }  
    return false;  
}  

三、公平锁 vs 非公平锁的源码差异

1. 非公平锁(NonfairSync)

final boolean nonfairTryAcquire(int acquires) {  
    Thread current = Thread.currentThread();  
    int c = getState();  
    if (c == 0) {  
        if (compareAndSetState(0, acquires)) { // 直接CAS,允许插队  
            setExclusiveOwnerThread(current);  
            return true;  
        }  
    } else if (current == getExclusiveOwnerThread()) { // 可重入  
        setState(c + acquires);  
        return true;  
    }  
    return false;  
}  

2. 公平锁(FairSync)

protected final boolean tryAcquire(int acquires) {  
    Thread current = Thread.currentThread();  
    int c = getState();  
    if (c == 0) {  
        if (!hasQueuedPredecessors() && // 检查队列是否有等待线程  
            compareAndSetState(0, acquires)) { // 无等待时才CAS  
            setExclusiveOwnerThread(current);  
            return true;  
        }  
    } else if (current == getExclusiveOwnerThread()) { // 可重入  
        setState(c + acquires);  
        return true;  
    }  
    return false;  
}  

四、高级功能与使用场景

1. 可中断锁获取(lockInterruptibly)

ReentrantLock lock = new ReentrantLock();  
try {  
    lock.lockInterruptibly(); // 响应中断  
    // 临界区代码  
} catch (InterruptedException e) {  
    // 处理中断逻辑  
} finally {  
    lock.unlock();  
}  

2. 超时尝试获取锁(tryLock)

if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待1秒  
    try {  
        // 临界区代码  
    } finally {  
        lock.unlock();  
    }  
} else {  
    // 超时处理  
}  

3. 条件变量(Condition)

对比synchronized的wait/notify:支持多个条件变量,实现精细化线程协作。
示例:生产者-消费者模型

ReentrantLock lock = new ReentrantLock();  
Condition notFull = lock.newCondition(); // 队列未满条件  
Condition notEmpty = lock.newCondition(); // 队列非空条件  

// 生产者  
lock.lock();  
try {  
    while (queue.isFull()) {  
        notFull.await(); // 等待队列未满  
    }  
    queue.add(item);  
    notEmpty.signal(); // 唤醒消费者  
} finally {  
    lock.unlock();  
}  

// 消费者  
lock.lock();  
try {  
    while (queue.isEmpty()) {  
        notEmpty.await(); // 等待队列非空  
    }  
    item = queue.remove();  
    notFull.signal(); // 唤醒生产者  
} finally {  
    lock.unlock();  
}  

五、最佳实践与常见问题

1. 必须手动释放锁

错误示例(可能导致死锁):

lock.lock();  
// 若此处抛出异常,锁无法释放!  
lock.unlock();  

正确做法:

lock.lock();  
try {  
    // 临界区代码  
} finally {  
    lock.unlock(); // 确保锁释放  
}  

2. 避免嵌套锁导致死锁

风险代码:

lockA.lock();  
try {  
    lockB.lock(); // 若另一线程先获取lockB,会死锁  
} finally {  
    lockB.unlock();  
    lockA.unlock();  
}  

解决方案:按固定顺序获取锁(如先锁A后锁B)。

3. 性能调优建议

  • 优先使用非公平锁:默认策略,适合大多数场景。
  • 减少锁粒度:采用分段锁(如ConcurrentHashMap的分段设计)。
  • 监控锁竞争:使用jstack或JFR分析线程阻塞情况。

六、ReentrantLock vs synchronized

特性 ReentrantLock synchronized
实现方式 JDK类,基于AQS JVM内置关键字
锁获取方式 显式调用lock()/unlock() 隐式获取(代码块/方法)
公平性 支持公平/非公平锁 仅非公平锁
可中断性 支持(lockInterruptibly) 不支持
超时机制 支持(tryLock) 不支持
条件变量 支持多个Condition 单一wait/notify
性能 高竞争下更优(可配置策略) Java 6后优化,低竞争下接近
代码复杂度 高(需手动管理锁) 低(自动释放)

七、源码分析:AQS的等待队列

// AQS中的Node类(简化版)  
static final class Node {  
    volatile int waitStatus;      // 等待状态(CANCELLED=1, SIGNAL=-1等)  
    volatile Node prev;           // 前驱节点  
    volatile Node next;           // 后继节点  
    volatile Thread thread;       // 关联的线程  
    Node nextWaiter;              // 条件队列中的下一个节点  
}  
// CLH 队列示意图
Head -> Node(Thread1, SIGNAL) ↔ Node(Thread2, CANCELLED) ↔ Tail

八、总结

设计亮点

  • 基于AQS的模板模式:将锁逻辑委托给子类(公平锁/非公平锁)实现。
  • 分离公平性策略:通过不同Sync子类支持灵活的锁分配机制。
  • 条件变量精细化控制:解决synchronized单一等待集的局限性。

适用场景

  • 需要可中断、超时或公平锁的高并发场景。
  • 复杂线程协作场景(如生产者-消费者模型、线程池任务调度)。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
分享到

ReentrantLock深度解析

也可使用浏览器菜单中的「分享」功能

微信扫一扫分享

标签: AQS JAVA Lock ReentrantLock 公平锁 可重入锁 锁 非公平锁 高并发
最后更新:2025年5月30日

岁月同一天 11 月 7 日

回望过去的今天,你在写什么

  • AI
    1 年前 2025年11月7日
    DeepSeek本地部署全攻略:从环境搭建到高级应用,打造专属 AI 助手

    DeepSeek 作为国产开源大模型的佼佼者,凭借出色的推理能力、代码生成效率和中文支持,成为很多开发者本地部署的首选。…

相关文章
  • JAVA设计模式-工厂方法模式2020年2月3日
  • 查看占用 CPU 最高的线程(Java)2021年5月27日
  • Aeron详解2026年8月13日
  • Java设计模式:策略模式2025年5月22日
  • Spring Boot 2.x使用PostgreSQL数据库2021年10月9日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >
1234567891112131415161718192021222324252627282930313233343536373839404142434446474849505152535455575859606162636465666769727476777879808182858687909293949596979899
取消回复
…

文章评论

还没有评论,快来抢沙发吧~

每一个不曾起舞的日子,都是对生命的辜负。

听点儿音乐吧 朋友~
文章目录
最新 热点 随机
最新 热点 随机
给服务器加了WAF 又一个逆天改命的人:小小徐 AI的生成能力等于生产力吗? NGINX严重漏洞(CVE-2026-15176、CVE-2026-93479、CVE-2026-97205) Kratos-plus v1.1.27更新说明 php2shell远程执行漏洞修复(CVE-2026-109618 和 CVE-2026-109619)
给主题增加了游客中心功能心气没了,干什么都不得劲我觉得是时候公布我的导航网站了给服务器加了WAF游本昌去世关于主题加载速度优化的一点儿小演进
Kratos+ v1.1.14版本更新说明 Wails 深度实战指南:用 Go + 前端技术打造轻量桌面应用(从入门到部署) ConcurrentHashMap常用方法源码解析(jdk1.8) 妈呀,昨天晚上睡觉做了一晚上的梦,可累死我了 HBASE初探 日积月累的力量|读《刻意练习》有感
最近评论
fairy 发布于 19 小时前(11月06日) 机器人快来我这里吧!
李锋镝 发布于 20 小时前(11月06日) 都是机器人,防不住
李锋镝 发布于 20 小时前(11月06日) 是的,太烦了
李锋镝 发布于 20 小时前(11月06日) 可以同一台服务器
fairy 发布于 21 小时前(11月06日) 这流量,好厉害啊。我一年都没那么多
标签聚合
分布式 SQL Redis 数据库 Theme IDEA SpringBoot WordPress Spring Claude AI 日常 架构 JVM AI编程 K8s ElasticSearch JAVA 多线程 MySQL
友情链接
  • sssr7844的博客
  • 皮皮社
  • 韩小韩博客
  • 瓦匠个人小站
  • 懋和道人
  • 老张博客
  • 彬红茶日记
  • 搬砖日记
  • 知向前端
  • 刘永博客
  • lijie blog
  • Serendipity
  • 九仞之行
  • 志文工作室
  • 林羽凡
  • 哥斯拉
  • 韩情脉脉
  • Mr.Sun的博客
  • Honesty

COPYRIGHT © 2016-2026 lifengdi.com. ALL RIGHTS RESERVED.

lifengdi.com 十年之约虫洞

Domain age badge for lifengdi.com

Theme Kratos-plus By Dylan Li

津ICP备2024022503号-3

京公网安备11011502039375号