Java 面试题
目录

Java 中 synchronized 和 ReentrantLock 的区别是什么?

· 约 4 分钟读完 · 第 62 / 274 章

推荐答案

在 Java 中,synchronized 和 ReentrantLock 都是用于实现线程同步的机制,但它们有以下主要区别:

  1. 锁的获取与释放:

    • synchronized:锁的获取和释放是隐式的,由 JVM 自动管理。进入同步代码块时自动获取锁,退出时自动释放锁。
    • ReentrantLock:锁的获取和释放是显式的,需要手动调用 lock() 和 unlock() 方法。
  2. 可中断性:

    • synchronized:不支持中断等待锁的线程。
    • ReentrantLock:支持中断等待锁的线程,可以通过 lockInterruptibly() 方法实现。
  3. 公平性:

    • synchronized:不保证公平性,锁的获取是非公平的。
    • ReentrantLock:可以选择公平锁或非公平锁,通过构造函数参数指定。
  4. 条件变量:

    • synchronized:只能通过 wait()、notify() 和 notifyAll() 方法实现简单的条件等待。
    • ReentrantLock:支持多个条件变量,可以通过 newCondition() 方法创建多个 Condition 对象。
  5. 性能:

    • synchronized:在低竞争情况下性能较好,但在高竞争情况下性能可能下降。
    • ReentrantLock:在高竞争情况下性能较好,但需要手动管理锁的释放。

本题详细解读

1. 锁的获取与释放

synchronized 是 Java 内置的关键字,用于实现同步代码块或方法。当一个线程进入 synchronized 代码块时,它会自动获取锁,退出时自动释放锁。这种方式简单易用,但缺乏灵活性。

ReentrantLock 是 java.util.concurrent.locks 包中的一个类,提供了更灵活的锁机制。使用 ReentrantLock 时,需要显式地调用 lock() 方法来获取锁,并在使用完毕后调用 unlock() 方法释放锁。这种方式虽然增加了代码的复杂性,但提供了更多的控制能力。

2. 可中断性

synchronized 不支持中断等待锁的线程。如果一个线程在等待锁时被中断,它仍然会继续等待,直到获取锁为止。

ReentrantLock 提供了 lockInterruptibly() 方法,允许线程在等待锁的过程中响应中断。如果线程在等待锁时被中断,它会抛出 InterruptedException,从而可以提前结束等待。

3. 公平性

synchronized 不保证锁的公平性,锁的获取是非公平的。这意味着等待时间较长的线程不一定优先获得锁。

ReentrantLock 提供了公平锁和非公平锁的选择。公平锁会按照线程等待的顺序分配锁,而非公平锁则允许插队。公平锁可以减少线程饥饿问题,但可能会降低吞吐量。

4. 条件变量

synchronized 通过 wait()、notify() 和 notifyAll() 方法实现条件等待。这种方式适用于简单的同步场景,但在复杂的同步需求下可能不够灵活。

ReentrantLock 提供了 Condition 接口,可以通过 newCondition() 方法创建多个条件变量。每个条件变量可以独立地控制线程的等待和唤醒,适用于复杂的同步场景。

5. 性能

在低竞争情况下,synchronized 的性能通常优于 ReentrantLock,因为 synchronized 是 JVM 内置的机制,优化较好。

在高竞争情况下,ReentrantLock 的性能可能优于 synchronized,因为 ReentrantLock 提供了更多的控制选项,如可中断锁、公平锁等,可以减少线程争用和上下文切换的开销。

综上所述,synchronized 和 ReentrantLock 各有优缺点,选择哪种机制取决于具体的应用场景和需求。