news 2026/4/15 18:10:09

<span class=“js_title_inner“>Java中ReentrantLock的完整使用指南</span>

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
<span class=“js_title_inner“>Java中ReentrantLock的完整使用指南</span>

Java中ReentrantLock的完整使用指南

作者:系统管理员

摘要

Java中ReentrantLock的完整使用指南


Java中ReentrantLock的完整使用指南(附实战案例)

ReentrantLock是JUC(java.util.concurrent)包下的显式可重入锁,相比synchronized提供了更灵活的锁控制能力(公平锁、可中断、超时获取、精准唤醒等)。以下从「基础用法」「核心特性」「高级场景」三个维度,结合实战代码讲解其使用方式。

一、核心前置知识

  1. 核心API

  • lock()

    :阻塞获取锁(必须在finally中释放);

  • unlock()

    :释放锁(必须手动调用,否则锁泄漏);

  • tryLock()

    :非阻塞获取锁,获取成功返回true,失败返回false;

  • tryLock(long timeout, TimeUnit unit)

    :超时获取锁;

  • lockInterruptibly()

    :可中断获取锁;

  • newCondition()

    :创建条件变量(精准唤醒线程)。

  • 使用原则

    • 锁的释放必须放在finally块中(避免异常导致锁无法释放);

    • 重入时需保证「获取次数=释放次数」(否则锁不会真正释放);

    • 公平锁需在构造器指定(new ReentrantLock(true)),默认非公平锁。

    二、基础用法(必掌握)

    1. 最基本的加锁/释放锁

    适用于简单同步场景,替代synchronized,核心是「lock() + try + finally + unlock()」:

    import java.util.concurrent.locks.ReentrantLock; public class BasicReentrantLockDemo { // 1. 创建ReentrantLock实例(默认非公平锁) private final ReentrantLock lock = new ReentrantLock(); private int count = 0; // 2. 加锁方法 public void increment() { // 获取锁(阻塞式,直到获取成功) lock.lock(); try { // 线程安全的业务逻辑 count++; System.out.println(Thread.currentThread().getName() + ":count = " + count); // 演示可重入:同一线程再次获取锁 reentrantMethod(); } finally { // 必须在finally中释放锁,避免异常导致锁泄漏 lock.unlock(); } } // 重入方法(同一把锁) private void reentrantMethod() { lock.lock(); // 重入获取锁,计数+1 try { System.out.println(Thread.currentThread().getName() + ":重入成功,计数=" + lock.getHoldCount()); } finally { lock.unlock(); // 重入释放锁,计数-1 } } public static void main(String[] args) { BasicReentrantLockDemo demo = new BasicReentrantLockDemo(); // 多线程测试 for (int i = 0; i < 3; i++) { new Thread(demo::increment, "线程" + (i + 1)).start(); } } }

    输出结果(线程安全,count递增,重入计数正常):

    线程1:count = 1 线程1:重入成功,计数=2 线程2:count = 2 线程2:重入成功,计数=2 线程3:count = 3 线程3:重入成功,计数=2

    2. 公平锁的使用

    公平锁保证「线程按请求锁的顺序获取锁」(避免插队),适合排队场景(如秒杀、任务队列):

    public class FairReentrantLockDemo { // 创建公平锁(构造器传true) private final ReentrantLock fairLock = new ReentrantLock(true); public void doTask() { fairLock.lock(); try { System.out.println(Thread.currentThread().getName() + " 获取公平锁,执行任务"); // 模拟业务耗时 Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { fairLock.unlock(); System.out.println(Thread.currentThread().getName() + " 释放公平锁"); } } public static void main(String[] args) { FairReentrantLockDemo demo = new FairReentrantLockDemo(); // 多个线程竞争公平锁,按顺序获取 for (int i = 0; i < 5; i++) { new Thread(demo::doTask, "公平锁线程" + (i + 1)).start(); } } }

    输出特点:线程按启动顺序依次获取锁(无插队),但性能略低于非公平锁。

    三、高级特性(重点)

    1. 非阻塞获取锁(tryLock())

    避免线程永久阻塞,获取不到锁时直接返回,适合“尝试执行,失败则降级”的场景:

    public class TryLockDemo { private final ReentrantLock lock = new ReentrantLock(); public boolean tryDoTask() { // 非阻塞获取锁:成功返回true,失败返回false if (lock.tryLock()) { try { System.out.println(Thread.currentThread().getName() + " 获取锁成功,执行任务"); Thread.sleep(500); // 模拟业务 return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } finally { lock.unlock(); } } else { // 获取锁失败,执行降级逻辑 System.out.println(Thread.currentThread().getName() + " 获取锁失败,执行降级逻辑"); return false; } } public static void main(String[] args) { TryLockDemo demo = new TryLockDemo(); // 线程1先获取锁,线程2获取失败 new Thread(demo::tryDoTask, "T1").start(); new Thread(demo::tryDoTask, "T2").start(); } }

    输出结果

    T1 获取锁成功,执行任务 T2 获取锁失败,执行降级逻辑

    2. 超时获取锁(tryLock(timeout, unit))

    指定时间内获取不到锁则放弃,避免线程无限等待,适合“限时任务”场景:

    import java.util.concurrent.TimeUnit; public class TimeoutLockDemo { private final ReentrantLock lock = new ReentrantLock(); public boolean doTaskWithTimeout() { try { // 尝试1秒内获取锁,超时返回false if (lock.tryLock(1, TimeUnit.SECONDS)) { try { System.out.println(Thread.currentThread().getName() + " 超时获取锁成功"); // 模拟耗时2秒的任务(超过超时时间) Thread.sleep(2000); return true; } finally { lock.unlock(); } } else { System.out.println(Thread.currentThread().getName() + " 1秒内未获取到锁,放弃执行"); return false; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println(Thread.currentThread().getName() + " 获取锁时被中断"); return false; } } public static void main(String[] args) { TimeoutLockDemo demo = new TimeoutLockDemo(); // 线程1先获取锁,线程2超时失败 new Thread(demo::doTaskWithTimeout, "T1").start(); new Thread(demo::doTaskWithTimeout, "T2").start(); } }

    输出结果

    T1 超时获取锁成功 T2 1秒内未获取到锁,放弃执行

    3. 可中断获取锁(lockInterruptibly())

    线程等待锁时,可响应interrupt()中断请求,避免线程永久阻塞,适合“可取消任务”场景:

    public class InterruptibleLockDemo { private final ReentrantLock lock = new ReentrantLock(); public void doInterruptibleTask() { try { // 可中断获取锁:等待时收到中断信号,抛出InterruptedException lock.lockInterruptibly(); try { System.out.println(Thread.currentThread().getName() + " 获取锁成功,执行任务"); Thread.sleep(1000); // 模拟业务 } finally { lock.unlock(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态 System.out.println(Thread.currentThread().getName() + " 获取锁时被中断,任务取消"); } } public static void main(String[] args) throws InterruptedException { InterruptibleLockDemo demo = new InterruptibleLockDemo(); Thread t1 = new Thread(demo::doInterruptibleTask, "T1"); Thread t2 = new Thread(demo::doInterruptibleTask, "T2"); t1.start(); t2.start(); Thread.sleep(100); // 让t1先获取锁,t2进入等待 t2.interrupt(); // 中断t2的锁等待 } }

    输出结果

    T1 获取锁成功,执行任务 T2 获取锁时被中断,任务取消

    4. 条件变量(Condition):精准唤醒线程

    替代Object的wait()/notify(),支持多个条件队列,精准唤醒指定线程(如生产者-消费者模型):

    import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; // 生产者-消费者模型:精准唤醒 public class ConditionDemo { private final ReentrantLock lock = new ReentrantLock(); // 生产者条件:队列满时等待 private final Condition producerCond = lock.newCondition(); // 消费者条件:队列空时等待 private final Condition consumerCond = lock.newCondition(); private final int[] queue = new int[5]; // 固定大小队列 private int head = 0, tail = 0, count = 0; // 队列指针和计数 // 生产者:生产数据 public void produce(int data) throws InterruptedException { lock.lock(); try { // 队列满时,生产者等待 while (count == queue.length) { System.out.println(Thread.currentThread().getName() + " 队列满,等待生产"); producerCond.await(); // 生产者进入等待队列 } // 生产数据 queue[tail] = data; tail = (tail + 1) % queue.length; count++; System.out.println(Thread.currentThread().getName() + " 生产:" + data + ",队列大小:" + count); // 精准唤醒消费者(而非随机唤醒) consumerCond.signal(); } finally { lock.unlock(); } } // 消费者:消费数据 public int consume() throws InterruptedException { lock.lock(); try { // 队列空时,消费者等待 while (count == 0) { System.out.println(Thread.currentThread().getName() + " 队列空,等待消费"); consumerCond.await(); // 消费者进入等待队列 } // 消费数据 int data = queue[head]; head = (head + 1) % queue.length; count--; System.out.println(Thread.currentThread().getName() + " 消费:" + data + ",队列大小:" + count); // 精准唤醒生产者 producerCond.signal(); return data; } finally { lock.unlock(); } } public static void main(String[] args) { ConditionDemo demo = new ConditionDemo(); // 启动生产者线程 new Thread(() -> { for (int i = 1; i <= 10; i++) { try { demo.produce(i); Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }, "生产者").start(); // 启动消费者线程 new Thread(() -> { for (int i = 1; i <= 10; i++) { try { demo.consume(); Thread.sleep(200); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }, "消费者").start(); } }

    输出特点:生产者/消费者按需精准唤醒,无无效唤醒,效率更高。

    四、常见坑点与注意事项

    1. 忘记在finally中释放锁
      后果:异常时锁无法释放,导致其他线程永久阻塞;
      解决:严格遵循「lock() → try → 业务 → finally → unlock()」。

    2. 重入次数与释放次数不匹配
      示例:获取2次锁,仅释放1次 → 锁计数=1,不会真正释放;
      解决:保证「获取次数=释放次数」,可通过lock.getHoldCount()检查。

    3. 公平锁滥用
      公平锁需维护等待队列,性能比非公平锁低30%左右;
      解决:仅在“必须按顺序获取锁”时使用公平锁,默认用非公平锁。

    4. Condition.await()未在循环中判断条件
      后果:虚假唤醒(spurious wakeup)导致逻辑错误;
      解决:始终用while(条件不满足)包裹await()(如生产者-消费者示例)。

    五、适用场景总结

    用法

    适用场景

    lock() + unlock()

    简单同步场景,替代synchronized

    tryLock()

    非阻塞获取锁,失败降级

    tryLock(timeout)

    限时获取锁,避免永久等待

    lockInterruptibly()

    可取消任务,响应中断

    Condition

    精准唤醒线程(生产者-消费者、多条件等待)

    公平锁

    排队场景(秒杀、任务队列)

    核心原则:简单场景用synchronized,需要高级特性时用ReentrantLock,且使用ReentrantLock时务必保证锁的正确释放。


    原文链接: https://1024bat.cn/article/43

    来源: 淘书1024bat

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 6:28:26

<span class=“js_title_inner“>谈持之以恒</span>

1、水滴石穿&#xff0c;在每刻一点一点的滴下去&#xff0c;风浪不可能一直很大&#xff0c;水滴却可以一直持续下去。一口吃不成大胖子&#xff0c;消化系统有它的负荷&#xff0c;超过负荷系统就无法持久运转。2、白天抽空就休息保持松静均乐放松的状态&#xff0c;这样压力…

作者头像 李华
网站建设 2026/4/14 1:22:08

springboot基于Vue+Golang的视频娱乐网站-开题报告

目录 项目背景与意义技术选型依据核心功能模块创新点关键技术指标预期成果 项目技术支持可定制开发之功能亮点源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作 项目背景与意义 随着互联网技术发展&#xff0c;视频娱乐平台需求日益增长。传…

作者头像 李华
网站建设 2026/3/30 10:05:52

设计师福音!Nano-Banana轻松搞定产品拆解视图

设计师福音&#xff01;Nano-Banana轻松搞定产品拆解视图 导语 你有没有过这样的时刻&#xff1a;盯着一件新到的运动鞋&#xff0c;忍不住把它拆开研究——拉链怎么嵌入&#xff1f;中底泡棉怎么贴合大底&#xff1f;鞋舌内衬用的是什么材质&#xff1f;又或者面对一款轻薄笔…

作者头像 李华
网站建设 2026/4/13 10:18:39

RexUniNLU零样本模型入门必看:无需微调的NER/分类/情感分析全流程

RexUniNLU零样本模型入门必看&#xff1a;无需微调的NER/分类/情感分析全流程 你是不是也遇到过这些场景&#xff1a; 想快速从一段新闻里抽人名、地名、公司名&#xff0c;但没时间标注训练数据&#xff1f;客服对话要自动打上“投诉”“咨询”“表扬”标签&#xff0c;可业…

作者头像 李华