面试题答案
一键面试可重入锁
- 概念:
- 可重入锁,也叫递归锁,指的是同一个线程在外层方法获取了锁,在进入该线程的内层方法时同样可以获取该锁,不需要重新获取。例如,一个线程在执行一个同步方法A,该方法中又调用了另一个同步方法B,此时如果使用可重入锁,线程无需再次获取锁就可以进入方法B。在Java中,
synchronized
关键字修饰的方法或代码块以及ReentrantLock
都是可重入锁。
- 可重入锁,也叫递归锁,指的是同一个线程在外层方法获取了锁,在进入该线程的内层方法时同样可以获取该锁,不需要重新获取。例如,一个线程在执行一个同步方法A,该方法中又调用了另一个同步方法B,此时如果使用可重入锁,线程无需再次获取锁就可以进入方法B。在Java中,
- 实现机制:
synchronized
:JVM通过为每个对象维护一个锁计数器来实现可重入性。当线程第一次获取锁时,锁计数器设为1。若该线程再次进入同步块,锁计数器递增。线程每退出一次同步块,锁计数器递减,当计数器为0时,锁被释放。ReentrantLock
:ReentrantLock
内部使用AQS(AbstractQueuedSynchronizer)框架来实现。AQS维护一个FIFO队列来管理等待获取锁的线程,同时通过一个状态变量(state
)来记录锁的持有情况。当线程获取锁时,若state
为0,表示锁未被占用,线程获取锁并将state
设为1;若state
不为0且当前线程是持有锁的线程,则将state
加1,实现可重入。释放锁时,将state
减1,当state
为0时,锁被完全释放。
- 应用场景:
- 适用于大多数多线程同步场景,尤其是在一个线程需要多次获取同一把锁的情况下,比如在复杂的业务逻辑中,一个方法调用链中可能多次涉及到对同一资源的同步访问。可重入锁避免了死锁的发生,因为线程可以多次获取自己已经持有的锁,而不会因为重复获取锁而陷入死循环等待。
公平锁
- 概念:
- 公平锁是指在锁的竞争中,等待时间最长的线程会优先获得锁。即按照请求锁的先后顺序来分配锁,遵循FIFO原则。这样可以保证所有线程都有机会获得锁,不会出现某些线程长时间等待而得不到锁的情况。
- 实现机制:
ReentrantLock
:ReentrantLock
通过构造函数的参数来控制是否为公平锁,ReentrantLock(boolean fair)
,当fair
为true
时创建公平锁。在公平锁的实现中,ReentrantLock
的tryAcquire
方法会首先检查同步队列中是否有等待时间更长的线程。如果有,则当前线程会进入同步队列等待,而不是直接尝试获取锁。这样就保证了锁的分配是公平的。synchronized
:synchronized
是非公平锁,它在锁可用时,不会考虑等待队列中线程的等待顺序,而是让等待队列中的线程随机竞争获取锁。
- 应用场景:
- 适用于对线程公平性要求较高的场景,比如在一些资源分配场景中,需要确保每个线程都能按照请求顺序获得资源,避免某些线程长时间得不到资源而导致饥饿现象。但公平锁由于需要维护等待队列和检查等待顺序,性能相对非公平锁较低,因为频繁的线程切换和队列操作会带来额外的开销。
区别
- 实现机制区别:
- 可重入锁主要通过锁计数器(
synchronized
)或AQS状态变量(ReentrantLock
)来实现线程对同一锁的多次获取;而公平锁主要通过维护等待队列并按照FIFO原则分配锁(如ReentrantLock
的公平锁实现),synchronized
不具备公平性实现机制。
- 可重入锁主要通过锁计数器(
- 应用场景区别:
- 可重入锁适用于大多数需要避免死锁的多线程同步场景,注重线程对同一资源的多次访问安全性;公平锁适用于对线程公平性要求高,希望避免线程饥饿的场景,但可能会牺牲一定的性能。性能方面,可重入锁(如
ReentrantLock
的非公平模式和synchronized
)通常比公平锁性能更好,因为公平锁的公平性维护需要额外的开销。
- 可重入锁适用于大多数需要避免死锁的多线程同步场景,注重线程对同一资源的多次访问安全性;公平锁适用于对线程公平性要求高,希望避免线程饥饿的场景,但可能会牺牲一定的性能。性能方面,可重入锁(如