一、核心概述

ReentrantLock 是 Java JUC(java.util.concurrent)包下的可重入独占锁,基于 AQS(AbstractQueuedSynchronizer)实现,相比 synchronized 具备更灵活的锁控制能力(如可中断、可超时、公平 / 非公平锁切换),是并发编程中替代 synchronized 的核心工具。

二、核心 API

1. 构造方法

方法 说明
ReentrantLock() 默认构造,创建非公平锁(性能更优,默认推荐)
ReentrantLock(boolean fair) 手动指定锁类型:- true:公平锁(按等待队列顺序获取锁,避免饥饿)- false:非公平锁(抢占式,可能插队)

2. 核心操作方法

方法 说明
void lock() 获取锁,若锁被占用则阻塞(不可中断)
void lockInterruptibly() 获取锁,支持线程中断(阻塞时可响应 interrupt ())
boolean tryLock() 尝试获取锁,立即返回结果:- true:获取成功- false:获取失败(不阻塞)
boolean tryLock(long timeout, TimeUnit unit) 超时获取锁:在指定时间内尝试获取,超时 / 中断则返回 false
void unlock() 释放锁(必须与 lock 配对,建议放在 finally 中)
Condition newCondition() 创建条件变量,实现精准的线程等待 / 唤醒(替代 Object.wait/notify)
boolean isLocked() 判断锁是否被占用
boolean isFair() 判断是否为公平锁
boolean isHeldByCurrentThread() 判断当前线程是否持有该锁
int getHoldCount() 获取当前线程持有该锁的重入次数

三、使用示例

1. 基础可重入使用

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockBasicDemo {
    // 创建非公平锁(默认)
    private static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        // 启动两个线程竞争锁
        new Thread(ReentrantLockBasicDemo::doWork, "Thread-1").start();
        new Thread(ReentrantLockBasicDemo::doWork, "Thread-2").start();
    }

    private static void doWork() {
        lock.lock(); // 获取锁
        try {
            System.out.println(Thread.currentThread().getName() + " 获取锁,重入次数:" + lock.getHoldCount());
            // 演示可重入:同一线程再次获取锁
            reentrantMethod();
            Thread.sleep(1000); // 模拟业务逻辑
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock(); // 释放锁,重入次数减1
            System.out.println(Thread.currentThread().getName() + " 释放锁,重入次数:" + lock.getHoldCount());
        }
    }

    private static void reentrantMethod() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 重入方法,重入次数:" + lock.getHoldCount());
        } finally {
            lock.unlock();
        }
    }
}

输出结果

Thread-1 获取锁,重入次数:1
Thread-1 重入方法,重入次数:2
Thread-1 释放锁,重入次数:1
Thread-1 释放锁,重入次数:0
Thread-2 获取锁,重入次数:1
Thread-2 重入方法,重入次数:2
Thread-2 释放锁,重入次数:1
Thread-2 释放锁,重入次数:0

2. 公平锁 vs 非公平锁

import java.util.concurrent.locks.ReentrantLock;

public class FairVsUnfairLockDemo {
    // 公平锁:new ReentrantLock(true)
    private static final ReentrantLock fairLock = new ReentrantLock(true);
    // 非公平锁:new ReentrantLock(false)
    private static final ReentrantLock unfairLock = new ReentrantLock(false);

    public static void main(String[] args) {
        // 测试公平锁
        System.out.println("=== 公平锁测试 ===");
        for (int i = 0; i < 5; i++) {
            new Thread(() -> acquireLock(fairLock), "Fair-Thread-" + i).start();
        }

        // 休眠2秒,确保公平锁测试完成
        try { Thread.sleep(2000); } catch (InterruptedException e) {}

        // 测试非公平锁
        System.out.println("=== 非公平锁测试 ===");
        for (int i = 0; i < 5; i++) {
            new Thread(() -> acquireLock(unfairLock), "Unfair-Thread-" + i).start();
        }
    }

    private static void acquireLock(ReentrantLock lock) {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 获取到锁");
            Thread.sleep(100); // 模拟业务耗时
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
}

核心差异

  • 公平锁:线程按等待顺序获取锁,输出顺序基本与线程启动顺序一致;
  • 非公平锁:线程可能插队获取锁,输出顺序随机(性能更高,减少上下文切换)。

3. Condition 条件变量使用

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockConditionDemo {
    private static final ReentrantLock lock = new ReentrantLock();
    // 创建两个条件变量:等待队列、通知队列
    private static final Condition waitCondition = lock.newCondition();
    private static boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        // 等待线程
        Thread waitThread = new Thread(() -> {
            lock.lock();
            try {
                // 条件不满足则等待
                while (!flag) {
                    System.out.println(Thread.currentThread().getName() + " 等待条件满足");
                    waitCondition.await(); // 释放锁并等待
                }
                System.out.println(Thread.currentThread().getName() + " 条件满足,执行业务");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }, "WaitThread");

        // 通知线程
        Thread notifyThread = new Thread(() -> {
            lock.lock();
            try {
                flag = true;
                System.out.println(Thread.currentThread().getName() + " 修改条件,唤醒等待线程");
                waitCondition.signal(); // 唤醒单个等待线程
            } finally {
                lock.unlock();
            }
        }, "NotifyThread");

        waitThread.start();
        Thread.sleep(1000); // 确保等待线程先执行
        notifyThread.start();
    }
}

输出结果

WaitThread 等待条件满足
NotifyThread 修改条件,唤醒等待线程
WaitThread 条件满足,执行业务

四、底层原理

1. 核心依赖:AQS(抽象队列同步器)

ReentrantLock 内部封装了 Sync 抽象类(继承 AQS),并实现了两个子类:

  • NonfairSync:非公平锁实现;
  • FairSync:公平锁实现。

AQS 核心结构:

  • 状态变量state(int 类型),0 表示锁空闲,>0 表示锁被持有(数值 = 重入次数);
  • CLH 队列:双向链表,存储等待获取锁的线程(Node 节点);
  • 独占模式:ReentrantLock 是独占锁,同一时刻仅一个线程持有锁。

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

3. 公平锁 vs 非公平锁核心差异

  • 非公平锁:加锁时直接 CAS 抢占,失败后再入队(可能插队,性能高);
  • 公平锁:加锁前先检查 CLH 队列是否有等待线程,若有则直接入队,不抢占(严格按顺序,性能低但公平)。

4. 解锁流程

  1. 调用 unlock() 时,state-1
  2. state 减至 0,释放独占线程,唤醒 CLH 队列头节点的后继线程;
  3. state > 0(重入未释放完),仅更新 state,不释放锁。

5. 可重入实现原理

加锁时通过 Thread.currentThread() == getExclusiveOwnerThread() 判断当前线程是否为持有锁的线程,若是则直接将 state+1,无需重新竞争;解锁时逐次 state-1,直到 state=0 才真正释放锁。

五、与 synchronized 对比

特性 ReentrantLock synchronized
可重入 支持 支持
锁类型 公平 / 非公平可配置 非公平锁
中断性 支持(lockInterruptibly ()) 不支持
超时获取 支持(tryLock (timeout)) 不支持
条件变量 支持多个(newCondition ()) 仅一个(对象监视器)
释放方式 手动 unlock(必须放 finally) 自动释放(出作用域)

总结

  1. ReentrantLock 是基于 AQS 的可重入独占锁,核心 API 涵盖锁的获取(lock/tryLock)、释放(unlock)、条件变量(Condition)等,使用时需手动保证 unlock 与 lock 配对;
  2. 底层通过 AQS 的 state 变量记录重入次数,CLH 队列管理等待线程,公平 / 非公平锁的核心差异在于加锁时是否直接抢占;
  3. 相比 synchronized,ReentrantLock 更灵活(支持中断、超时、多条件变量),但需手动释放锁,适合复杂并发场景。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐