Java中的锁机制:ReentrantLock与synchronized对比
字数 1866 2025-11-03 08:33:37

Java中的锁机制:ReentrantLock与synchronized对比

描述
在多线程编程中,锁是保证线程安全的核心工具。Java提供了两种主要的锁机制:synchronized关键字(内置锁)和ReentrantLock(显式锁)。虽然二者都能实现同步,但在设计理念、功能灵活性和底层实现上存在显著差异。理解它们的区别有助于在实际场景中做出合理选择。

知识要点分步讲解

1. 基础概念:锁的作用与可重入性

  • 锁的核心作用:确保同一时间只有一个线程能访问共享资源,避免数据竞争。
  • 可重入性:指线程可以重复获取自己已经持有的锁。例如,在递归方法或同步方法调用另一个同步方法时,若锁不可重入,会导致线程自我阻塞。synchronizedReentrantLock都是可重入锁。
    // synchronized的可重入示例
    public synchronized void methodA() {
        methodB(); // 线程可直接进入methodB的同步块,不会阻塞
    }
    public synchronized void methodB() { }
    

2. synchronized关键字详解

  • 用法
    • 修饰实例方法:锁对象是当前实例(this)。
    • 修饰静态方法:锁对象是当前类的Class对象(如MyClass.class)。
    • 同步代码块:需显式指定锁对象(如synchronized(obj))。
  • 特点
    • JVM内置实现:无需手动加锁/解锁,由JVM通过监视器(Monitor)管理。
    • 自动释放锁:线程执行完同步代码或发生异常时,JVM自动释放锁。
    • 非中断等待:线程在等待锁时无法被中断,可能造成死锁难以解除。

3. ReentrantLock显式锁详解

  • 用法:需显式创建锁对象,手动调用lock()unlock()
    ReentrantLock lock = new ReentrantLock();
    lock.lock();
    try {
        // 临界区代码
    } finally {
        lock.unlock(); // 必须放在finally中确保锁释放
    }
    
  • 核心优势
    • 可中断等待:通过lockInterruptibly()方法,等待锁的线程可响应中断。
    • 公平锁选项:构造函数传入true可创建公平锁(按等待时间分配锁),默认非公平锁(吞吐量更高)。
    • 条件变量(Condition):支持多个等待队列,例如在生产者-消费者模型中,可分别管理“队列满”和“队列空”的等待线程。
      Condition notEmpty = lock.newCondition();
      Condition notFull = lock.newCondition();
      

4. 功能对比与选择策略

特性 synchronized ReentrantLock
锁获取方式 隐式,JVM管理 显式,代码控制
中断响应 不支持 支持lockInterruptibly()
公平锁 非公平(JVM优化) 可配置公平性
条件变量 仅通过wait()/notify() 支持多个Condition
锁释放 自动释放 必须手动解锁,否则可能死锁

选择建议

  • 优先使用synchronized:代码简洁,JVM持续优化其性能(如锁升级机制)。
  • 需高级功能时选ReentrantLock:如公平锁、可中断、精确的条件控制。

5. 底层实现原理简析

  • synchronized
    • 通过对象头的Mark Word记录锁状态(无锁、偏向锁、轻量级锁、重量级锁)。
    • 锁升级过程:偏向锁(单线程)→ 轻量级锁(少量竞争)→ 重量级锁(激烈竞争)。
  • ReentrantLock
    • 基于AQS(AbstractQueuedSynchronizer)队列同步器,通过CAS操作维护线程排队。
    • 非公平锁直接尝试抢占,公平锁先检查队列是否有等待线程。

总结
synchronized是Java原生的轻量级同步工具,适用于大多数简单场景;ReentrantLock提供了更灵活的API,适合复杂的并发控制。理解二者的底层机制和适用场景,是编写高效、健壮多线程代码的基础。

Java中的锁机制:ReentrantLock与synchronized对比 描述 在多线程编程中,锁是保证线程安全的核心工具。Java提供了两种主要的锁机制: synchronized 关键字(内置锁)和 ReentrantLock (显式锁)。虽然二者都能实现同步,但在设计理念、功能灵活性和底层实现上存在显著差异。理解它们的区别有助于在实际场景中做出合理选择。 知识要点分步讲解 1. 基础概念:锁的作用与可重入性 锁的核心作用 :确保同一时间只有一个线程能访问共享资源,避免数据竞争。 可重入性 :指线程可以重复获取自己已经持有的锁。例如,在递归方法或同步方法调用另一个同步方法时,若锁不可重入,会导致线程自我阻塞。 synchronized 和 ReentrantLock 都是可重入锁。 2. synchronized关键字详解 用法 : 修饰实例方法:锁对象是当前实例( this )。 修饰静态方法:锁对象是当前类的Class对象(如 MyClass.class )。 同步代码块:需显式指定锁对象(如 synchronized(obj) )。 特点 : JVM内置实现 :无需手动加锁/解锁,由JVM通过监视器(Monitor)管理。 自动释放锁 :线程执行完同步代码或发生异常时,JVM自动释放锁。 非中断等待 :线程在等待锁时无法被中断,可能造成死锁难以解除。 3. ReentrantLock显式锁详解 用法 :需显式创建锁对象,手动调用 lock() 和 unlock() 。 核心优势 : 可中断等待 :通过 lockInterruptibly() 方法,等待锁的线程可响应中断。 公平锁选项 :构造函数传入 true 可创建公平锁(按等待时间分配锁),默认非公平锁(吞吐量更高)。 条件变量(Condition) :支持多个等待队列,例如在生产者-消费者模型中,可分别管理“队列满”和“队列空”的等待线程。 4. 功能对比与选择策略 | 特性 | synchronized | ReentrantLock | |---------------------|----------------------------------|----------------------------------------| | 锁获取方式 | 隐式,JVM管理 | 显式,代码控制 | | 中断响应 | 不支持 | 支持 lockInterruptibly() | | 公平锁 | 非公平(JVM优化) | 可配置公平性 | | 条件变量 | 仅通过 wait()/notify() | 支持多个Condition | | 锁释放 | 自动释放 | 必须手动解锁,否则可能死锁 | 选择建议 : 优先使用 synchronized :代码简洁,JVM持续优化其性能(如锁升级机制)。 需高级功能时选 ReentrantLock :如公平锁、可中断、精确的条件控制。 5. 底层实现原理简析 synchronized : 通过对象头的Mark Word记录锁状态(无锁、偏向锁、轻量级锁、重量级锁)。 锁升级过程:偏向锁(单线程)→ 轻量级锁(少量竞争)→ 重量级锁(激烈竞争)。 ReentrantLock : 基于AQS(AbstractQueuedSynchronizer)队列同步器,通过CAS操作维护线程排队。 非公平锁直接尝试抢占,公平锁先检查队列是否有等待线程。 总结 synchronized 是Java原生的轻量级同步工具,适用于大多数简单场景; ReentrantLock 提供了更灵活的API,适合复杂的并发控制。理解二者的底层机制和适用场景,是编写高效、健壮多线程代码的基础。