news 2026/10/15 1:48:56

Java中ReentrantLock的完整使用指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java中ReentrantLock的完整使用指南

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/10/15 17:03:15

StructBERT模型详解:中文情感分析原理与部署实战

StructBERT模型详解&#xff1a;中文情感分析原理与部署实战 1. 引言&#xff1a;中文情感分析的技术价值与挑战 在社交媒体、电商评论、客服对话等大量文本数据中&#xff0c;中文情感分析已成为企业洞察用户情绪、优化产品服务的关键技术。通过自动识别用户表达中的情绪倾向…

作者头像 李华
网站建设 2026/10/15 4:14:07

情感分析系统数据增强:文本生成应用

情感分析系统数据增强&#xff1a;文本生成应用 1. 中文情感分析的挑战与价值 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;情感分析&#xff08;Sentiment Analysis&#xff09;是理解用户情绪、优化产品反馈和提升客户服务的关键技术。尤其在中文语境下&…

作者头像 李华
网站建设 2026/10/14 21:49:09

StructBERT轻量级部署:情感分析API调参案例

StructBERT轻量级部署&#xff1a;情感分析API调参案例 1. 背景与应用场景 在中文自然语言处理领域&#xff0c;情感分析是一项基础且关键的任务。无论是社交媒体舆情监控、电商评论挖掘&#xff0c;还是客服对话情绪识别&#xff0c;准确判断用户表达的情绪倾向&#xff08;…

作者头像 李华
网站建设 2026/10/15 18:17:25

AI绘画商业应用测试:云端GPU按天租赁,灵活省钱

AI绘画商业应用测试&#xff1a;云端GPU按天租赁&#xff0c;灵活省钱 1. 为什么摄影工作室需要AI绘画 对于中小型摄影工作室来说&#xff0c;试水AI写真市场是个低成本验证商机的好方法。传统方式需要投入数万元购置高端显卡设备&#xff0c;而通过云端GPU按天租赁&#xff…

作者头像 李华
网站建设 2026/10/15 18:08:45

直面存在困境:存在主义精神分析学的核心洞见与人文救赎

直面存在困境&#xff1a;存在主义精神分析学的核心洞见与人文救赎在心理学与哲学的交汇地带&#xff0c;存在主义精神分析学以其独特的视角&#xff0c;打破了传统精神分析的生物决定论与实证心理学的机械论局限。它将存在主义哲学对 “人的存在本质” 的追问&#xff0c;与精…

作者头像 李华
网站建设 2026/10/15 18:08:32

Qwen大模型新手指南:没环境别怕,3步体验

Qwen大模型新手指南&#xff1a;没环境别怕&#xff0c;3步体验 1. 为什么选择Qwen大模型&#xff1f; 最近很多传统行业老板参加AI讲座后&#xff0c;都被大模型的能力震撼到了。但回到公司让员工研究时&#xff0c;往往卡在第一步&#xff1a;环境配置太复杂。显卡驱动、CU…

作者头像 李华