面试题答案
一键面试相同点
- 功能相似:二者都用于实现多线程环境下的同步,保证同一时间只有一个线程能够访问被保护的临界区,防止数据竞争和不一致问题。
- 都是可重入锁:同一个线程可以多次获取同一个锁而不会产生死锁。当线程获取锁后再次尝试获取该锁时,计数会增加,释放锁时计数减少,直到计数为0时锁完全释放。
不同点
- 锁升级过程
- Synchronized:在Java 6及以后,Synchronized 锁会从偏向锁开始,当有第二个线程访问时,升级为轻量级锁,如果竞争加剧,则进一步升级为重量级锁。偏向锁是指当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程进入和退出同步块时不需要进行CAS操作来加锁和解锁。轻量级锁是通过CAS操作尝试将对象头中的Mark Word替换为指向锁记录的指针,如果成功则获取锁,失败则膨胀为重量级锁。重量级锁依赖操作系统的互斥量,通过内核态和用户态的切换来实现同步,开销较大。
- ReentrantLock:没有类似Synchronized的锁升级过程。它始终是一种基于AQS(AbstractQueuedSynchronizer)框架实现的锁机制,线程竞争时直接通过AQS队列进行排队等待获取锁。
- JVM底层实现
- Synchronized:是Java语言层面的关键字,由JVM底层来实现其功能。在字节码层面,使用monitorenter和monitorexit指令来实现同步,这两个指令依赖于对象头中的Monitor对象。Monitor是一个依赖于操作系统Mutex Lock实现的重量级同步工具,底层是通过C++实现的。
- ReentrantLock:是Java类库中的一个类,基于AQS框架实现。AQS是一个用于构建锁和同步器的框架,通过一个FIFO队列来管理等待获取锁的线程。ReentrantLock通过内部类Sync继承AQS,实现了AQS的一些抽象方法来管理锁的获取和释放,其底层实现依赖于Unsafe类的CAS操作来实现线程间的同步。
- 锁优化策略
- Synchronized:通过锁升级的策略来优化性能,尽量减少重量级锁的使用,从而降低线程上下文切换的开销。偏向锁适用于只有一个线程访问同步块的场景,轻量级锁适用于竞争不激烈的场景,这样可以避免直接使用重量级锁带来的高开销。
- ReentrantLock:提供了一些额外的锁优化特性,例如公平锁和非公平锁的选择。公平锁会按照线程请求锁的顺序来分配锁,避免线程饥饿,但可能会降低吞吐量;非公平锁则允许线程在锁可用时直接尝试获取锁,而不考虑等待队列中的顺序,在一定程度上提高了吞吐量,但可能导致某些线程长时间无法获取锁。此外,ReentrantLock还支持锁中断、锁超时等功能,提供了更灵活的同步控制方式,开发者可以根据具体场景选择合适的策略来优化性能。