reentrantlock 无内置耗时统计,需通过 threadmxbean、装饰器封装(如 timedreentrantlock)、字节码增强(bytebuddy/asm)或 micrometer 等实现可观测性;生产中应聚焦 trylock 超时率、p99 耗时及队列长度,避免误用日志级别。

ReentrantLock 本身不提供内置的锁等待耗时统计能力,需结合 AOP、ThreadMXBean、自定义封装或第三方监控工具(如 Micrometer + Prometheus)来实现锁获取耗时的可观测性。
基于 ThreadMXBean 的实时锁等待检测
Java 的 ThreadMXBean 可以获取线程当前的阻塞状态与阻塞时间,适用于诊断“谁在等锁、等了多久”:
- 启用线程监控:JVM 启动时添加 -XX:+UnlockCommercialFeatures -XX:+FlightRecorder(JDK8u261+ 可省略商业特性参数),或确保 -Dcom.sun.management.jmxremote 开启
- 调用 ThreadMXBean.getThreadInfo(threads, true, true) 获取带锁信息的线程快照
- 检查 ThreadInfo.getLockName() 是否含 ReentrantLock 内部队列对象(如 AbstractQueuedSynchronizer$ConditionObject),并读取 getBlockedTime()(单位毫秒)
- 注意:getBlockedTime() 仅在 isThreadBlocked() 为 true 时有效,且该值是自上次进入阻塞态起的累计时间,非单次 acquire 耗时
封装 ReentrantLock 实现耗时埋点
最直接可控的方式是继承或代理 ReentrantLock,在 lock()、tryLock(long, TimeUnit) 等关键方法中记录耗时:
- 使用装饰器模式包装原 Lock,例如 TimedReentrantLock,内部持有一个 ReentrantLock 实例
- 在 lock() 中记录开始时间,成功获取后计算差值;对 tryLock(long, TimeUnit) 需区分超时/失败/成功三种路径分别打点
- 将耗时指标输出到 Metrics Registry(如 Dropwizard Metrics 或 Micrometer 的 Timer),支持分位数(p95/p99)、速率、异常计数等聚合分析
- 避免在锁内执行耗时操作(如远程上报),应异步提交指标或批量 flush
利用 JVM TI 或字节码增强做无侵入监控
适合已有大量遗留代码、无法修改 Lock 使用方式的场景:
- 通过 ByteBuddy 或 ASM 在类加载时增强 ReentrantLock.lock()、tryLock() 方法入口/出口
- 在增强逻辑中注入耗时统计:用 System.nanoTime() 记录前后时间戳,结合线程 ID 和锁实例哈希码做维度标识
- 配合 ThreadLocal 存储上下文(如业务 traceId),实现锁等待与业务链路关联
- 注意避免高频采样引发性能抖动,可配置采样率(如 1%)或仅对慢锁(>100ms)全量采集
生产建议与避坑点
真实环境中的锁监控不是越细越好,关键是定位瓶颈和预防雪崩:
- 优先监控 tryLock(timeout) 超时率和平均等待时间,比无参 lock() 更易暴露资源争用
- 不要仅看平均值,p99 和长尾(如 >1s)更能反映毛刺问题;建议搭配直方图(Histogram)而非简单 Timer
- ReentrantLock 的公平性设置(new ReentrantLock(true))会显著增加调度开销,开启后务必监控队列长度(getQueueLength())
- 避免把锁监控日志打在 ERROR 级别——等待本身不等于错误;推荐用 WARN + 结构化指标替代日志刷屏
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











