胡杨河市网站建设_网站建设公司_域名注册_seo优化
2026/1/11 13:16:42 网站建设 项目流程

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询