冬眠的笔记
首页文章分类书单项目关于
冬眠
X

© 2026 冬眠的笔记 · 用文字记录思考,用思考改变生活

首页>文章>Java
JavaJUCReentrantLock锁

ReentrantLock 源码解析及面试题

ReentrantLock 的公平锁/非公平锁实现、可重入机制、AQS 应用以及与 synchronized 的对比

冬眠
冬眠
专注于技术、阅读与思考
2025-11-19
发布日期
32 min read
阅读时长
浏览量
ReentrantLock 源码解析及面试题

概述

ReentrantLock是Java并发包java.util.concurrent.locks中的一个重要类,它提供了与synchronized关键字相同的基本行为和语义,但具有更强的功能。ReentrantLock基于AQS(AbstractQueuedSynchronizer)实现,支持重入、公平性选择、条件变量等高级特性。

主要特性

  • 重入性:同一线程可以多次获取同一把锁
  • 公平性:支持公平锁和非公平锁两种模式
  • 可中断:支持响应中断的锁获取
  • 超时机制:支持尝试获取锁的超时机制
  • 条件变量:支持多个条件变量(Condition)

ReentrantLock基础概念

基本使用方式

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void method() {
        lock.lock();
        try {
            // 临界区代码
            System.out.println("执行业务逻辑");
        } finally {
            lock.unlock(); // 必须在finally中释放锁
        }
    }
    
    // 可中断的锁获取
    public void interruptibleMethod() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            // 业务逻辑
        } finally {
            lock.unlock();
        }
    }
    
    // 尝试获取锁
    public void tryLockMethod() {
        if (lock.tryLock()) {
            try {
                // 业务逻辑
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("获取锁失败");
        }
    }
}

构造函数

// 默认非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}

// 可选择公平性
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

AQS核心原理深度解析

AQS简介

AbstractQueuedSynchronizer(AQS)是Java并发包的核心基础组件,为实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关同步器提供了一个框架。

AQS的核心组件

1. state状态字段

// AQS中的核心状态字段
private volatile int state;

// 获取状态
protected final int getState() {
    return state;
}

// 设置状态
protected final void setState(int newState) {
    state = newState;
}

// CAS操作更新状态
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

在ReentrantLock中,state的含义:

  • state = 0:锁未被任何线程持有
  • state = 1:锁被某个线程持有一次
  • state = n:锁被某个线程重入n次

2. CLH队列

AQS使用CLH(Craig, Landin, and Hagersten)队列的变种来管理等待线程:

// AQS中的节点定义
static final class Node {
    // 共享模式
    static final Node SHARED = new Node();
    // 独占模式
    static final Node EXCLUSIVE = null;
    
    // 节点状态常量
    static final int CANCELLED =  1; // 节点被取消
    static final int SIGNAL    = -1; // 后继节点需要被唤醒
    static final int CONDITION = -2; // 节点在条件队列中等待
    static final int PROPAGATE = -3; // 共享模式下的传播
    
    volatile int waitStatus;    // 等待状态
    volatile Node prev;         // 前驱节点
    volatile Node next;         // 后继节点
    volatile Thread thread;     // 关联的线程
    Node nextWaiter;           // 条件队列中的下一个节点
}

3. 独占锁的获取流程

// AQS中的acquire方法
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

流程说明:

  1. 调用tryAcquire()尝试获取锁
  2. 如果失败,调用addWaiter()将当前线程加入等待队列
  3. 调用acquireQueued()在队列中等待获取锁
  4. 如果在等待过程中被中断,则自我中断

ReentrantLock内部结构分析

类继承关系

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;
    
    // 内部抽象同步器
    abstract static class Sync extends AbstractQueuedSynchronizer {
        // 公共方法实现
    }
    
    // 非公平同步器
    static final class NonfairSync extends Sync {
        // 非公平锁实现
    }
    
    // 公平同步器
    static final class FairSync extends Sync {
        // 公平锁实现
    }
}

Sync抽象类

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;
    
    // 抽象方法,由子类实现
    abstract void lock();
    
    // 非公平方式尝试获取锁
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 锁未被持有,尝试CAS获取
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            // 重入情况
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    
    // 释放锁
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
}

锁获取过程源码解析

非公平锁获取过程

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    
    // 非公平锁的lock方法
    final void lock() {
        // 直接尝试CAS获取锁
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1); // 调用AQS的acquire方法
    }
    
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

公平锁获取过程

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
    
    final void lock() {
        acquire(1); // 直接调用AQS的acquire方法
    }
    
    // 公平锁的tryAcquire实现
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 检查是否有前驱节点在等待
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            // 重入情况
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

acquireQueued方法详解

// AQS中的acquireQueued方法
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            // 如果前驱是头节点,尝试获取锁
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 检查是否需要阻塞
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

锁释放过程源码解析

unlock方法

// ReentrantLock的unlock方法
public void unlock() {
    sync.release(1);
}

// AQS的release方法
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h); // 唤醒后继节点
        return true;
    }
    return false;
}

unparkSuccessor方法

// 唤醒后继节点
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 从尾部向前查找有效节点
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread); // 唤醒线程
}

重入机制实现原理

重入的实现

重入机制的核心在于:

  1. 使用state字段记录重入次数
  2. 使用exclusiveOwnerThread记录持有锁的线程
// 重入检查逻辑
if (current == getExclusiveOwnerThread()) {
    // 当前线程已持有锁,增加重入次数
    int nextc = c + acquires;
    if (nextc < 0) // 防止溢出
        throw new Error("Maximum lock count exceeded");
    setState(nextc);
    return true;
}

重入示例

public class ReentrantExample {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void method1() {
        lock.lock(); // state: 0 -> 1
        try {
            System.out.println("method1");
            method2(); // 重入调用
        } finally {
            lock.unlock(); // state: 1 -> 0
        }
    }
    
    public void method2() {
        lock.lock(); // state: 1 -> 2 (重入)
        try {
            System.out.println("method2");
        } finally {
            lock.unlock(); // state: 2 -> 1
        }
    }
}

公平锁vs非公平锁对比

实现差异

特性 公平锁 非公平锁
获取策略 严格按照FIFO顺序 允许"插队"
性能 较低(需要检查队列) 较高(直接尝试获取)
吞吐量 较低 较高
延迟 较高但稳定 较低但不稳定
饥饿问题 不会发生 可能发生

关键代码差异

// 公平锁:需要检查队列
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
    setExclusiveOwnerThread(current);
    return true;
}

// 非公平锁:直接尝试获取
if (compareAndSetState(0, acquires)) {
    setExclusiveOwnerThread(current);
    return true;
}

hasQueuedPredecessors方法

// 检查是否有前驱节点在等待
public final boolean hasQueuedPredecessors() {
    Node t = tail;
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

Condition条件变量详解

Condition接口

public interface Condition {
    void await() throws InterruptedException;
    void awaitUninterruptibly();
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal();
    void signalAll();
}

ConditionObject实现

// AQS内部的ConditionObject实现
public class ConditionObject implements Condition, java.io.Serializable {
    private transient Node firstWaiter; // 条件队列头节点
    private transient Node lastWaiter;  // 条件队列尾节点
    
    // await实现
    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter(); // 加入条件队列
        int savedState = fullyRelease(node); // 完全释放锁
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            LockSupport.park(this); // 阻塞等待
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }
    
    // signal实现
    public final void signal() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first); // 唤醒第一个等待节点
    }
}

生产者消费者示例

public class ProducerConsumer {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private final Object[] items = new Object[100];
    private int putptr, takeptr, count;
    
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length)
                notFull.await(); // 队列满时等待
            items[putptr] = x;
            if (++putptr == items.length) putptr = 0;
            ++count;
            notEmpty.signal(); // 通知消费者
        } finally {
            lock.unlock();
        }
    }
    
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0)
                notEmpty.await(); // 队列空时等待
            Object x = items[takeptr];
            if (++takeptr == items.length) takeptr = 0;
            --count;
            notFull.signal(); // 通知生产者
            return x;
        } finally {
            lock.unlock();
        }
    }
}

与synchronized全面对比

功能对比

特性 ReentrantLock synchronized
锁的实现 API层面 JVM层面
性能 高并发下更优 JDK6后优化显著
功能 功能丰富 功能简单
可中断性 支持 不支持
超时机制 支持 不支持
公平性 可选择 非公平
条件变量 支持多个 只有一个
重入性 支持 支持
异常处理 手动释放 自动释放

使用场景选择

选择ReentrantLock的场景:

  • 需要可中断的锁获取
  • 需要超时机制
  • 需要公平锁
  • 需要多个条件变量
  • 需要尝试获取锁

选择synchronized的场景:

  • 简单的同步需求
  • 不需要高级功能
  • 代码简洁性要求高
  • JVM自动优化

性能测试对比

public class PerformanceTest {
    private static final int THREAD_COUNT = 10;
    private static final int ITERATIONS = 1000000;
    
    // ReentrantLock测试
    private final ReentrantLock lock = new ReentrantLock();
    private int reentrantCounter = 0;
    
    public void reentrantLockTest() {
        lock.lock();
        try {
            reentrantCounter++;
        } finally {
            lock.unlock();
        }
    }
    
    // synchronized测试
    private int syncCounter = 0;
    
    public synchronized void synchronizedTest() {
        syncCounter++;
    }
}

性能分析和使用场景

性能特点

  1. 低竞争场景:synchronized和ReentrantLock性能相近
  2. 高竞争场景:ReentrantLock通常表现更好
  3. 公平锁:性能比非公平锁低10-20%
  4. 条件变量:比Object.wait/notify更高效

适用场景

ReentrantLock适用场景

// 1. 需要可中断的锁获取
public void interruptibleTask() {
    try {
        lock.lockInterruptibly();
        try {
            // 长时间运行的任务
            longRunningTask();
        } finally {
            lock.unlock();
        }
    } catch (InterruptedException e) {
        // 处理中断
        Thread.currentThread().interrupt();
    }
}

// 2. 需要超时机制
public boolean tryWithTimeout() {
    try {
        if (lock.tryLock(5, TimeUnit.SECONDS)) {
            try {
                // 业务逻辑
                return true;
            } finally {
                lock.unlock();
            }
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    return false;
}

// 3. 需要公平性保证
public class FairQueue<T> {
    private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
    private final Queue<T> queue = new LinkedList<>();
    
    public void enqueue(T item) {
        lock.lock();
        try {
            queue.offer(item);
        } finally {
            lock.unlock();
        }
    }
}

常见面试题集锦

1. ReentrantLock与synchronized的区别?

答案要点:

  • 实现层面:ReentrantLock是API层面,synchronized是JVM层面
  • 功能丰富性:ReentrantLock支持可中断、超时、公平性、多条件变量
  • 性能:高并发下ReentrantLock通常更优
  • 使用复杂度:synchronized更简单,ReentrantLock需要手动释放

2. 什么是AQS?ReentrantLock如何基于AQS实现?

答案要点:

  • AQS是AbstractQueuedSynchronizer,提供了同步器的基础框架
  • 核心组件:state状态字段、CLH队列、独占/共享模式
  • ReentrantLock通过继承AQS的Sync类实现
  • 重写tryAcquire和tryRelease方法定义锁的获取和释放逻辑

3. 公平锁和非公平锁的实现差异?

答案要点:

  • 公平锁:严格按照FIFO顺序,需要检查hasQueuedPredecessors()
  • 非公平锁:允许"插队",直接尝试CAS获取锁
  • 性能差异:非公平锁吞吐量更高,公平锁延迟更稳定
  • 使用场景:公平锁适合对顺序敏感的场景

4. ReentrantLock如何实现重入?

答案要点:

  • 使用state字段记录重入次数
  • 使用exclusiveOwnerThread记录持有锁的线程
  • 同一线程再次获取锁时,增加state计数
  • 释放锁时,减少state计数,直到为0才真正释放

5. Condition的工作原理?

答案要点:

  • Condition基于条件队列实现
  • await()方法:释放锁,加入条件队列,阻塞等待
  • signal()方法:将条件队列中的节点移到同步队列
  • 支持多个条件变量,比Object.wait/notify更灵活

6. 如何避免死锁?

死锁问题分析

**对称锁死锁(Symmetric Lock Deadlock)**是最常见的死锁场景:

public class SymmetricLockDeadlockDemo {
    private final ReentrantLock lock1 = new ReentrantLock();
    private final ReentrantLock lock2 = new ReentrantLock();
    
    // 线程1执行此方法
    public void method1() {
        lock1.lock();
        try {
            System.out.println("Thread1 acquired lock1");
            Thread.sleep(100); // 模拟业务处理
            
            lock2.lock(); // 尝试获取lock2,可能导致死锁
            try {
                System.out.println("Thread1 acquired lock2");
            } finally {
                lock2.unlock();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock1.unlock();
        }
    }
    
    // 线程2执行此方法
    public void method2() {
        lock2.lock();
        try {
            System.out.println("Thread2 acquired lock2");
            Thread.sleep(100); // 模拟业务处理
            
            lock1.lock(); // 尝试获取lock1,可能导致死锁
            try {
                System.out.println("Thread2 acquired lock1");
            } finally {
                lock1.unlock();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock2.unlock();
        }
    }
}

活锁问题(Livelock)

活锁是另一种并发问题,线程不会被阻塞,但会不断改变状态以响应其他线程:

public class ReentrantLockLivelockDemo {
    private final ReentrantLock lock1 = new ReentrantLock();
    private final ReentrantLock lock2 = new ReentrantLock();
    
    public void politeWorker1() {
        while (true) {
            lock1.lock();
            try {
                if (lock2.tryLock()) {
                    try {
                        // 成功获取两个锁,执行业务逻辑
                        System.out.println("Worker1 completed task");
                        break;
                    } finally {
                        lock2.unlock();
                    }
                } else {
                    // 获取lock2失败,"礼貌地"释放lock1
                    System.out.println("Worker1 backing off");
                }
            } finally {
                lock1.unlock();
            }
            
            // 短暂等待后重试
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
    
    public void politeWorker2() {
        while (true) {
            lock2.lock();
            try {
                if (lock1.tryLock()) {
                    try {
                        // 成功获取两个锁,执行业务逻辑
                        System.out.println("Worker2 completed task");
                        break;
                    } finally {
                        lock1.unlock();
                    }
                } else {
                    // 获取lock1失败,"礼貌地"释放lock2
                    System.out.println("Worker2 backing off");
                }
            } finally {
                lock2.unlock();
            }
            
            // 短暂等待后重试
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
 }

线程池死锁(Cyclic Thread Pool Deadlock)

线程池中的循环依赖也可能导致死锁,特别是当任务之间存在相互等待的情况:

public class CyclicThreadPoolDeadLockDemo {
    private final ExecutorService executor1 = Executors.newFixedThreadPool(1);
    private final ExecutorService executor2 = Executors.newFixedThreadPool(1);
    
    public void demonstrateDeadlock() {
        // 在executor1中提交任务,该任务会向executor2提交子任务并等待结果
        Future<String> future1 = executor1.submit(() -> {
            System.out.println("Task1 in executor1 started");
            
            // 向executor2提交任务并等待结果
            Future<String> future2 = executor2.submit(() -> {
                System.out.println("Task2 in executor2 started");
                
                // 再向executor1提交任务并等待结果 - 这里会死锁!
                Future<String> future3 = executor1.submit(() -> {
                    return "Task3 result";
                });
                
                try {
                    return future3.get(); // 等待executor1中的任务完成
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            
            try {
                return future2.get(); // 等待executor2中的任务完成
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        
        try {
            String result = future1.get(5, TimeUnit.SECONDS);
            System.out.println("Result: " + result);
        } catch (TimeoutException e) {
            System.err.println("Deadlock detected: Task timed out!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 使用CompletableFuture避免死锁
    public void avoidDeadlockWithCompletableFuture() {
        CompletableFuture<String> future = CompletableFuture
            .supplyAsync(() -> {
                System.out.println("Task1 started");
                return "intermediate result";
            }, executor1)
            .thenComposeAsync(result -> {
                System.out.println("Task2 started with: " + result);
                return CompletableFuture.supplyAsync(() -> {
                    System.out.println("Task3 started");
                    return "final result";
                }, executor1); // 可以安全地使用同一个executor
            }, executor2);
        
        try {
            String result = future.get(5, TimeUnit.SECONDS);
            System.out.println("Result: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void cleanup() {
        executor1.shutdown();
        executor2.shutdown();
    }
}

答案要点:

  • 按顺序获取锁:始终以相同顺序获取多个锁
  • 使用tryLock()避免无限等待:设置超时时间或立即返回
  • 使用lockInterruptibly()支持中断:允许线程被中断
  • 避免嵌套锁:减少锁的层次结构
  • 使用超时机制:tryLock(timeout, TimeUnit)
  • 检测死锁:使用JVM工具或编程方式检测
// 死锁预防:统一锁顺序
public class DeadlockPrevention {
    private final ReentrantLock lock1 = new ReentrantLock();
    private final ReentrantLock lock2 = new ReentrantLock();
    
    // 方法1和方法2都按相同顺序获取锁
    public void safeMethod1() {
        acquireLocksInOrder();
    }
    
    public void safeMethod2() {
        acquireLocksInOrder();
    }
    
    private void acquireLocksInOrder() {
        lock1.lock();
        try {
            lock2.lock();
            try {
                // 业务逻辑
            } finally {
                lock2.unlock();
            }
        } finally {
            lock1.unlock();
        }
    }
    
    // 使用超时机制避免死锁
    public boolean safeMethodWithTimeout() {
        boolean acquired1 = false, acquired2 = false;
        try {
            acquired1 = lock1.tryLock(1, TimeUnit.SECONDS);
            if (acquired1) {
                acquired2 = lock2.tryLock(1, TimeUnit.SECONDS);
                if (acquired2) {
                    // 业务逻辑
                    return true;
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            if (acquired2) lock2.unlock();
            if (acquired1) lock1.unlock();
        }
        return false;
    }
}

7. ReentrantLock的性能优化建议?

性能测试对比

以下是ReentrantLock与synchronized在不同场景下的性能对比:

public class LockPerformanceTest {
    private final ReentrantLock reentrantLock = new ReentrantLock();
    private final ReentrantLock fairLock = new ReentrantLock(true);
    private final Object syncLock = new Object();
    private volatile int counter = 0;
    
    // 测试方法:ReentrantLock(非公平)
    public void testReentrantLock(int iterations) {
        for (int i = 0; i < iterations; i++) {
            reentrantLock.lock();
            try {
                counter++;
            } finally {
                reentrantLock.unlock();
            }
        }
    }
    
    // 测试方法:ReentrantLock(公平)
    public void testFairLock(int iterations) {
        for (int i = 0; i < iterations; i++) {
            fairLock.lock();
            try {
                counter++;
            } finally {
                fairLock.unlock();
            }
        }
    }
    
    // 测试方法:synchronized
    public void testSynchronized(int iterations) {
        for (int i = 0; i < iterations; i++) {
            synchronized (syncLock) {
                counter++;
            }
        }
    }
    
    // 性能测试结果(仅供参考,实际结果依赖于硬件和JVM):
    // 单线程场景(1,000,000次操作):
    // - synchronized: ~15ms
    // - ReentrantLock(非公平): ~18ms
    // - ReentrantLock(公平): ~25ms
    //
    // 高竞争场景(10个线程,每个100,000次操作):
    // - synchronized: ~200ms
    // - ReentrantLock(非公平): ~180ms
    // - ReentrantLock(公平): ~350ms
}

优化策略详解

答案要点:

  1. 减少锁的持有时间
// ❌ 锁持有时间过长
public void inefficientMethod() {
    lock.lock();
    try {
        // 耗时的计算
        String result = expensiveCalculation();
        // 网络IO操作
        sendToRemoteService(result);
        // 更新共享状态
        updateSharedState(result);
    } finally {
        lock.unlock();
    }
}

// ✅ 优化后:只在必要时持有锁
public void efficientMethod() {
    // 耗时操作在锁外进行
    String result = expensiveCalculation();
    sendToRemoteService(result);
    
    // 只在更新共享状态时持有锁
    lock.lock();
    try {
        updateSharedState(result);
    } finally {
        lock.unlock();
    }
}
  1. 减少锁的粒度
// ❌ 粗粒度锁
public class CoarseGrainedLocking {
    private final ReentrantLock lock = new ReentrantLock();
    private final Map<String, Object> data = new HashMap<>();
    private final List<String> logs = new ArrayList<>();
    
    public void updateData(String key, Object value) {
        lock.lock();
        try {
            data.put(key, value);
        } finally {
            lock.unlock();
        }
    }
    
    public void addLog(String message) {
        lock.lock();
        try {
            logs.add(message);
        } finally {
            lock.unlock();
        }
    }
}

// ✅ 细粒度锁
public class FineGrainedLocking {
    private final ReentrantLock dataLock = new ReentrantLock();
    private final ReentrantLock logLock = new ReentrantLock();
    private final Map<String, Object> data = new HashMap<>();
    private final List<String> logs = new ArrayList<>();
    
    public void updateData(String key, Object value) {
        dataLock.lock();
        try {
            data.put(key, value);
        } finally {
            dataLock.unlock();
        }
    }
    
    public void addLog(String message) {
        logLock.lock();
        try {
            logs.add(message);
        } finally {
            logLock.unlock();
        }
    }
}
  1. 选择合适的公平性策略

    • 非公平锁:吞吐量更高,适合高性能场景
    • 公平锁:延迟更稳定,适合对响应时间敏感的场景
  2. 使用tryLock()避免阻塞

public boolean nonBlockingUpdate(String key, Object value) {
    if (lock.tryLock()) {
        try {
            data.put(key, value);
            return true;
        } finally {
            lock.unlock();
        }
    }
    return false; // 获取锁失败,可以执行备选逻辑
}

// 带超时的tryLock
public boolean timedUpdate(String key, Object value) {
    try {
        if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
            try {
                data.put(key, value);
                return true;
            } finally {
                lock.unlock();
            }
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    return false;
}
  1. 考虑使用读写锁ReadWriteLock
    • 读多写少的场景下,ReadWriteLock性能更优
    • 允许多个读线程同时访问,提高并发度

最佳实践和注意事项

1. 正确的使用模式

// ✅ 正确的使用方式
public void correctUsage() {
    lock.lock();
    try {
        // 业务逻辑
    } finally {
        lock.unlock(); // 必须在finally中释放
    }
}

// ❌ 错误的使用方式
public void incorrectUsage() {
    lock.lock();
    // 业务逻辑
    lock.unlock(); // 可能因为异常导致锁无法释放
}

2. 避免常见陷阱

// ❌ 忘记释放锁
public void forgotUnlock() {
    lock.lock();
    if (someCondition) {
        return; // 忘记释放锁
    }
    lock.unlock();
}

// ❌ 重复释放锁
public void duplicateUnlock() {
    lock.lock();
    try {
        // 业务逻辑
        lock.unlock(); // 错误:在try块中释放
    } finally {
        lock.unlock(); // 重复释放,抛出IllegalMonitorStateException
    }
}

// ✅ 正确处理异常情况
public void correctExceptionHandling() {
    lock.lock();
    try {
        // 可能抛出异常的业务逻辑
        riskyOperation();
    } catch (Exception e) {
        // 处理异常
        handleException(e);
    } finally {
        lock.unlock(); // 确保锁被释放
    }
}

3. 性能优化建议

// 1. 减少锁的持有时间
public void optimizedLocking() {
    // 准备工作在锁外进行
    Object data = prepareData();
    
    lock.lock();
    try {
        // 只在必要时持有锁
        updateSharedState(data);
    } finally {
        lock.unlock();
    }
    
    // 后续处理在锁外进行
    postProcess();
}

// 2. 使用tryLock避免阻塞
public boolean nonBlockingOperation() {
    if (lock.tryLock()) {
        try {
            // 业务逻辑
            return true;
        } finally {
            lock.unlock();
        }
    }
    return false; // 获取锁失败,执行备选方案
}

// 3. 合理选择公平性
public class OptimizedService {
    // 高吞吐量场景使用非公平锁
    private final ReentrantLock highThroughputLock = new ReentrantLock(false);
    
    // 需要顺序保证的场景使用公平锁
    private final ReentrantLock fairOrderLock = new ReentrantLock(true);
}

4. 监控和调试

基础监控方法

public class LockMonitor {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void monitorLock() {
        System.out.println("队列长度: " + lock.getQueueLength());
        System.out.println("是否有线程等待: " + lock.hasQueuedThreads());
        System.out.println("是否被当前线程持有: " + lock.isHeldByCurrentThread());
        System.out.println("持有计数: " + lock.getHoldCount());
        System.out.println("是否公平锁: " + lock.isFair());
        System.out.println("是否被锁定: " + lock.isLocked());
    }
}

高级监控工具

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class AdvancedLockMonitor {
    private final ReentrantLock lock = new ReentrantLock();
    private final AtomicLong lockAcquisitions = new AtomicLong(0);
    private final AtomicLong lockContentions = new AtomicLong(0);
    private final AtomicLong totalWaitTime = new AtomicLong(0);
    private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    
    public void lockWithMonitoring() {
        long startTime = System.nanoTime();
        boolean acquired = false;
        
        try {
            // 尝试立即获取锁
            if (lock.tryLock()) {
                acquired = true;
                lockAcquisitions.incrementAndGet();
            } else {
                // 记录竞争
                lockContentions.incrementAndGet();
                
                // 阻塞获取锁
                lock.lock();
                acquired = true;
                lockAcquisitions.incrementAndGet();
                
                long waitTime = System.nanoTime() - startTime;
                totalWaitTime.addAndGet(waitTime);
            }
            
            // 执行业务逻辑
            doWork();
            
        } finally {
            if (acquired) {
                lock.unlock();
            }
        }
    }
    
    public void printStatistics() {
        System.out.println("=== 锁统计信息 ===");
        System.out.println("总获取次数: " + lockAcquisitions.get());
        System.out.println("竞争次数: " + lockContentions.get());
        System.out.println("竞争率: " + 
            String.format("%.2f%%", 
                (double) lockContentions.get() / lockAcquisitions.get() * 100));
        System.out.println("平均等待时间: " + 
            TimeUnit.NANOSECONDS.toMicros(totalWaitTime.get() / Math.max(1, lockContentions.get())) + "μs");
        
        // 当前锁状态
        System.out.println("当前队列长度: " + lock.getQueueLength());
        System.out.println("当前持有线程: " + 
            (lock.isLocked() ? "Thread-" + Thread.currentThread().getId() : "无"));
    }
    
    private void doWork() {
        // 模拟业务逻辑
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

死锁检测工具

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class DeadlockDetector {
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    
    public void startDeadlockDetection() {
        scheduler.scheduleAtFixedRate(this::checkForDeadlocks, 0, 5, TimeUnit.SECONDS);
    }
    
    private void checkForDeadlocks() {
        long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
        
        if (deadlockedThreads != null && deadlockedThreads.length > 0) {
            System.err.println("检测到死锁!涉及线程数: " + deadlockedThreads.length);
            
            ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads);
            for (ThreadInfo threadInfo : threadInfos) {
                System.err.println("死锁线程: " + threadInfo.getThreadName());
                System.err.println("线程状态: " + threadInfo.getThreadState());
                System.err.println("阻塞在: " + threadInfo.getLockName());
                System.err.println("持有锁: " + threadInfo.getLockOwnerId());
                
                StackTraceElement[] stackTrace = threadInfo.getStackTrace();
                for (StackTraceElement element : stackTrace) {
                    System.err.println("\t" + element.toString());
                }
                System.err.println();
            }
            
            // 可以选择记录日志、发送告警等
            handleDeadlock(threadInfos);
        }
    }
    
    private void handleDeadlock(ThreadInfo[] deadlockedThreads) {
        // 处理死锁的策略
        // 1. 记录详细日志
        // 2. 发送告警通知
        // 3. 尝试中断某些线程(谨慎使用)
        System.err.println("正在处理死锁情况...");
    }
    
    public void shutdown() {
        scheduler.shutdown();
    }
}

JVM参数监控

在生产环境中,可以使用以下JVM参数来监控锁的行为:

# 启用锁竞争监控
-XX:+UseBiasedLocking
-XX:+PrintGCDetails
-XX:+PrintConcurrentLocks

# 使用JFR(Java Flight Recorder)监控
-XX:+FlightRecorder
-XX:StartFlightRecording=duration=60s,filename=lock-monitoring.jfr

# 启用详细的同步统计
-XX:+PrintGCApplicationStoppedTime
-XX:+TraceBiasedLocking

使用JConsole和VisualVM

// 为了便于JConsole监控,可以实现MBean接口
public interface LockMonitorMBean {
    long getLockAcquisitions();
    long getLockContentions();
    double getContentionRate();
    long getAverageWaitTime();
    int getCurrentQueueLength();
}

public class LockMonitorMBeanImpl implements LockMonitorMBean {
    private final AdvancedLockMonitor monitor;
    
    public LockMonitorMBeanImpl(AdvancedLockMonitor monitor) {
        this.monitor = monitor;
    }
    
    @Override
    public long getLockAcquisitions() {
        return monitor.lockAcquisitions.get();
    }
    
    @Override
    public long getLockContentions() {
        return monitor.lockContentions.get();
    }
    
    @Override
    public double getContentionRate() {
        long acquisitions = getLockAcquisitions();
        return acquisitions > 0 ? (double) getLockContentions() / acquisitions * 100 : 0;
    }
    
    @Override
    public long getAverageWaitTime() {
        long contentions = getLockContentions();
        return contentions > 0 ? 
            TimeUnit.NANOSECONDS.toMicros(monitor.totalWaitTime.get() / contentions) : 0;
    }
    
    @Override
    public int getCurrentQueueLength() {
        return monitor.lock.getQueueLength();
    }
}

总结

ReentrantLock作为Java并发编程中的重要工具,是理解Java并发机制的关键。通过本文的深入分析,我们全面了解了其设计原理、实现细节和最佳实践。

核心特性回顾

  1. 可重入性(Reentrant)

    • 同一线程可以多次获取同一把锁
    • 通过state字段记录重入次数
    • 避免了线程自己阻塞自己的问题
  2. 公平性选择(Fairness)

    • 非公平锁:性能更高,但可能导致线程饥饿
    • 公平锁:保证FIFO顺序,延迟更稳定
    • 根据业务需求选择合适的策略
  3. 可中断性(Interruptible)

    • lockInterruptibly()支持响应中断
    • 避免线程无限期等待
    • 提供更好的程序控制能力
  4. 超时机制(Timeout)

    • tryLock(timeout)支持带超时的锁获取
    • 防止死锁和长时间阻塞
    • 提供降级处理机制
  5. 条件变量(Condition)

    • 提供比wait/notify更强大的线程协调机制
    • 支持多个条件队列
    • 精确控制线程的唤醒

技术实现要点

AQS框架的巧妙设计

// AQS的核心思想
- state字段:表示同步状态(锁状态 + 重入次数)
- CLH队列:管理等待线程的FIFO队列
- 模板方法:tryAcquire/tryRelease由子类实现
- CAS操作:保证状态变更的原子性

关键源码理解

  • 非公平锁:直接尝试CAS,失败后进入队列
  • 公平锁:先检查队列,再尝试获取
  • 重入机制:通过exclusiveOwnerThread和state计数实现
  • 释放过程:递减state,为0时真正释放锁

性能特点分析

场景 synchronized ReentrantLock(非公平) ReentrantLock(公平)
单线程 最优 略慢 最慢
低竞争 优秀 相当 较慢
高竞争 较慢 优秀 慢
功能性 基础 丰富 丰富

最佳实践总结

✅ 推荐做法

// 1. 标准的锁使用模式
lock.lock();
try {
    // 业务逻辑
} finally {
    lock.unlock();
}

// 2. 使用tryLock避免死锁
if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
    try {
        // 业务逻辑
    } finally {
        lock.unlock();
    }
} else {
    // 降级处理
}

// 3. 合理的锁粒度
private final ReentrantLock dataLock = new ReentrantLock();
private final ReentrantLock logLock = new ReentrantLock();

❌ 避免的陷阱

// 1. 忘记释放锁
lock.lock();
// 业务逻辑
// 忘记unlock() - 导致死锁

// 2. 锁粒度过粗
synchronized(this) {
    expensiveCalculation(); // 耗时操作不应在锁内
    updateSharedState();
}

// 3. 对称锁死锁
// Thread1: lock1 -> lock2
// Thread2: lock2 -> lock1

面试重点梳理

高频问题

  1. ReentrantLock与synchronized的区别?

    • 功能:ReentrantLock更丰富(公平性、中断、超时)
    • 性能:场景相关,高竞争下ReentrantLock可能更优
    • 使用:synchronized更简单,ReentrantLock更灵活
  2. 什么是AQS?ReentrantLock如何基于AQS实现?

    • AQS是同步器框架,提供状态管理和队列管理
    • ReentrantLock通过继承AQS,实现tryAcquire/tryRelease
    • 利用state字段和CLH队列完成锁的获取和释放
  3. 公平锁和非公平锁的区别?

    • 获取策略:公平锁检查队列,非公平锁直接尝试
    • 性能影响:非公平锁吞吐量更高,公平锁延迟更稳定
    • 适用场景:根据业务对公平性和性能的要求选择

深度问题

  1. ReentrantLock的重入是如何实现的?

    • exclusiveOwnerThread记录持有线程
    • state字段记录重入次数
    • 每次重入递增state,释放时递减
  2. 如何避免死锁?

    • 统一锁顺序
    • 使用tryLock超时机制
    • 避免嵌套锁
    • 使用死锁检测工具

学习建议

进阶路径

  1. 源码阅读:深入理解AQS框架设计
  2. 实践应用:在项目中合理使用ReentrantLock
  3. 性能调优:学会监控和优化锁的性能
  4. 扩展学习:了解其他AQS实现(Semaphore、CountDownLatch等)

相关技术

  • StampedLock:Java 8引入的高性能读写锁
  • LockSupport:更底层的线程阻塞和唤醒工具
  • CompletableFuture:异步编程,减少锁的使用
  • Actor模型:无锁并发编程思想

结语

ReentrantLock不仅是一个实用的并发工具,更是理解Java并发编程精髓的窗口。通过掌握其原理和最佳实践,我们能够:

  • 写出更安全、高效的并发代码
  • 深入理解Java并发框架的设计思想
  • 在面试中展现扎实的并发编程功底
  • 为学习更高级的并发技术打下基础

并发编程是一门艺术,需要在理论学习和实践应用中不断提升。希望本文能够帮助你在Java并发编程的道路上更进一步!

文章标签

JavaJUCReentrantLock锁源码分析
独占锁和共享锁
上一篇

独占锁和共享锁

2025-11-19

AbstractQueuedSynchronizer (AQS) 详解
下一篇

AbstractQueuedSynchronizer (AQS) 详解

2025-11-19

冬眠

冬眠

博主

专注于技术、阅读与思考。在这里记录学习、思考与生活。

116
文章
3
分类
关注我
系列:Java 锁

第 3 篇,共 6 篇

上一篇

AbstractQueuedSynchronizer (AQS) 详解

下一篇

独占锁和共享锁

文章目录

目录

  • 概述
  • ReentrantLock基础概念
  • AQS核心原理深度解析
  • ReentrantLock内部结构分析
  • 锁获取过程源码解析
  • 锁释放过程源码解析
  • 重入机制实现原理
  • 公平锁vs非公平锁对比
  • Condition条件变量详解
  • 与synchronized全面对比
  • 性能分析和使用场景
  • 常见面试题集锦
  • 最佳实践和注意事项
  • 总结

相关文章

查看更多
JUC 中的锁

JUC 中的锁

2025-11-19 · 11 min read

独占锁和共享锁

独占锁和共享锁

2025-11-19 · 32 min read

AbstractQueuedSynchronizer (AQS) 详解

AbstractQueuedSynchronizer (AQS) 详解

2025-11-19 · 23 min read