MST
星途 面试题库

面试题:Java多线程环境下synchronized关键字与ReentrantLock的对比

在Java多线程编程里,synchronized关键字和ReentrantLock都能用于实现线程同步以保障线程安全。请详细对比这两者在使用场景、性能、锁的获取与释放机制、锁的公平性等方面的区别,并说明在何种情况下应优先选择使用ReentrantLock。
39.3万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

使用场景

  • synchronized:可用于修饰方法和代码块,在普通的同步场景,如简单的对象同步操作中使用较为方便,它是Java内置的关键字,和代码耦合度高。
  • ReentrantLock:通常用于更复杂的同步场景,例如需要手动控制锁的获取与释放、实现公平锁或需要进行锁的中断等情况,使用起来更加灵活。

性能

  • synchronized:在Java 6之前性能较差,因为其是重量级锁,会引起线程上下文切换等开销。但Java 6之后,对其进行了优化,引入了偏向锁、轻量级锁等机制,性能得到了很大提升。在竞争不激烈的情况下,性能表现良好。
  • ReentrantLock:在竞争激烈的情况下,性能可能优于synchronized。它支持更细粒度的控制,如可以使用非公平锁来提高吞吐量(默认是非公平锁)。并且在高竞争场景下,ReentrantLock的锁获取和释放操作可以通过CAS等无锁机制实现,减少线程上下文切换的开销。

锁的获取与释放机制

  • synchronized:当线程进入同步代码块或方法时自动获取锁,当代码块或方法执行完毕(正常结束或抛出异常)时自动释放锁,程序员无法手动控制锁的释放时机。
  • ReentrantLock:通过调用lock()方法获取锁,需要手动调用unlock()方法释放锁,通常将unlock()放在finally块中以确保锁一定会被释放,防止死锁。例如:
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // 业务逻辑
} finally {
    lock.unlock();
}

锁的公平性

  • synchronized:是非公平锁,线程竞争锁时,不保证等待时间最长的线程优先获取锁,这种方式在一定程度上可以提高吞吐量。
  • ReentrantLock:默认是非公平锁,但可以通过构造函数设置为公平锁。公平锁会按照线程等待的先后顺序分配锁,能保证线程等待的公平性,但公平锁的实现开销较大,会降低系统的吞吐量。例如:
// 创建公平锁
ReentrantLock fairLock = new ReentrantLock(true); 

优先选择ReentrantLock的情况

  • 需要手动控制锁的释放:当需要在特定条件下提前释放锁,或者在不同的代码路径下释放锁时,ReentrantLock更合适,因为它可以手动调用unlock()方法。
  • 需要实现公平锁:如果业务场景要求线程获取锁的顺序必须是公平的,即等待时间最长的线程优先获取锁,那么应选择ReentrantLock并设置为公平锁。
  • 需要锁中断:当线程在获取锁的过程中需要能够响应中断时,ReentrantLock提供了lockInterruptibly()方法来实现此功能,而synchronized关键字无法做到。
  • 需要更细粒度的锁控制:例如需要使用读锁、写锁等功能(ReentrantLock可以配合Condition实现类似读写锁的功能),或者需要实现锁的超时获取等,ReentrantLock能提供更灵活的控制。