面试题答案
一键面试- 减少锁的粒度
- 策略描述:将大对象(可能被多个线程访问)拆分成多个小对象,每个小对象单独加锁。这样不同线程可以同时访问不同的小对象,从而减少锁竞争。
- 适用场景:适用于对象包含多个相对独立的部分,且不同部分被不同线程访问概率较高的场景。比如在一个电商系统中,商品对象包含商品基本信息、库存信息、价格信息等,不同业务模块对这些信息的操作相对独立,就可以对每个部分分别加锁。
- 锁粗化
- 策略描述:如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作出现在循环体中,此时可以将加锁的范围扩展(粗化)到整个操作序列的外部。这样减少了加锁解锁的次数,提升性能。
- 适用场景:适用于在一段代码中,频繁对同一对象进行加锁解锁操作的场景。例如,在一个方法中多次调用同一个对象的同步方法,就可以考虑锁粗化。
- 偏向锁
- 策略描述:偏向锁是为了在无竞争情况下减少锁获取的开销。当一个线程访问同步块并获取锁时,会在对象头中记录这个线程的 ID,以后该线程再次进入和退出同步块时,不需要进行 CAS 操作来加锁和解锁,只需要简单判断对象头的偏向锁标志位和线程 ID。
- 适用场景:适用于大部分时间只有一个线程访问同步块的场景。比如单线程环境下,或者在一个线程长时间持有锁的情况下,偏向锁能显著提升性能。
- 轻量级锁
- 策略描述:当锁是偏向锁时,来了第二个线程访问同步块,偏向锁就会升级为轻量级锁。轻量级锁在竞争不是特别激烈的情况下,通过 CAS 操作来尝试获取锁,避免了重量级锁使用操作系统互斥量带来的性能开销。
- 适用场景:适用于竞争不太激烈的场景,线程交替执行同步块的情况比较多,使用轻量级锁能减少线程阻塞和唤醒带来的开销。
- 自旋锁
- 策略描述:当一个线程尝试获取锁时,如果锁已经被其他线程持有,该线程不会立即阻塞,而是在原地循环等待一段时间,在这段时间内不断尝试获取锁,希望在等待期间持有锁的线程能释放锁,从而避免线程上下文切换的开销。
- 适用场景:适用于锁被持有时间较短,线程竞争不激烈的场景。因为如果自旋时间过长,反而会浪费 CPU 资源,不如直接阻塞线程。例如在一些简单的同步代码块,执行时间很短,自旋锁能有效提升性能。