当前位置: 面试刷题>> 当 Java 的 synchronized 升级到重量级锁时,会发生什么?


在Java并发编程中,synchronized 关键字扮演着至关重要的角色,它提供了一种简单而强大的机制来控制对共享资源的访问,以防止数据竞争和不一致性。synchronized 的实现经历了从轻量级锁(Lightweight Locking)到重量级锁(Heavyweight Locking)的转换过程,这种转换是JVM(Java虚拟机)根据锁的竞争情况自动进行的优化策略之一。下面,我将从高级程序员的视角详细解释这一过程,并辅以示例代码来说明。

轻量级锁与重量级锁

轻量级锁:当多个线程尝试访问同一个由synchronized保护的代码块时,JVM会首先尝试使用轻量级锁。轻量级锁的设计旨在减少使用互斥锁(mutex)的开销,特别是当锁的竞争不激烈时。它通过CAS(Compare-And-Swap)操作来尝试获取锁,如果成功,则线程继续执行;如果失败,则根据具体情况可能升级为重量级锁。

重量级锁:当轻量级锁无法有效管理锁的竞争时(例如,多个线程频繁地尝试获取同一个锁),JVM会将锁升级为重量级锁。重量级锁依赖于操作系统的互斥量(mutex)来实现,这意味着线程在获取锁时会进入阻塞状态,直到锁被释放。这种机制虽然确保了线程安全,但会引入较高的性能开销,因为线程阻塞和唤醒涉及系统调用和上下文切换。

锁升级的过程

锁升级的具体过程对程序员来说是透明的,但理解其背后的机制有助于我们编写更高效的并发代码。以下是一个简化的概念模型,用于说明锁升级的过程:

  1. 无锁状态:默认状态下,对象锁处于无锁状态,任何线程都可以尝试通过CAS操作获得锁。
  2. 轻量级锁:当第一个线程成功通过CAS操作获得锁时,锁进入轻量级锁状态。此时,如果有其他线程尝试获取锁,会尝试通过自旋(spin)等待锁被释放,而不是立即阻塞。
  3. 锁膨胀:如果自旋尝试的次数超过了一定阈值(这个阈值取决于JVM实现),或者JVM检测到有线程在等待锁(可能通过线程堆栈的监测),则JVM会将锁从轻量级锁升级为重量级锁。
  4. 重量级锁:锁升级后,线程将不再通过CAS操作或自旋来尝试获取锁,而是直接进入阻塞状态,等待锁被持有者释放。

示例代码

虽然直接展示锁升级的代码示例是不可能的(因为它发生在JVM内部),但我们可以编写一个可能触发锁升级的并发场景来演示其效果。

public class LockUpgradingDemo {
    private final Object lock = new Object();

    public void criticalSection() {
        synchronized (lock) {
            // 模拟耗时操作
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            // 访问或修改共享资源
        }
    }

    public static void main(String[] args) {
        LockUpgradingDemo demo = new LockUpgradingDemo();

        // 创建多个线程模拟锁竞争
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.criticalSection();
                }
            }).start();
        }
    }
}

在上述示例中,我们创建了一个LockUpgradingDemo类,其中包含一个由synchronized保护的criticalSection方法。在main方法中,我们创建了多个线程来频繁调用criticalSection方法,模拟锁的竞争。随着线程数量的增加和锁竞争的加剧,JVM可能会将锁从轻量级锁升级为重量级锁。

总结

了解synchronized的锁升级机制是Java并发编程中的一项重要技能。虽然锁升级对程序员来说是透明的,但理解其背后的原理可以帮助我们编写出更加高效、可扩展的并发代码。在编写高并发应用时,我们应尽量减少锁的使用,或者通过其他并发工具(如java.util.concurrent包中的类)来优化锁的性能。此外,对于性能敏感的应用,还可以考虑使用性能分析工具来监控锁的竞争情况,以便及时进行优化。在探索和学习Java并发编程的过程中,码小课(假设为学习资源或平台)可以为你提供丰富的资源和实战案例,帮助你更深入地理解和掌握这些高级概念。

推荐面试题