返回

深入剖析 JVM 中 synchronized 的锁状态:详解线程同步

后端

在Java中,synchronized关键字是实现多线程安全的重要工具之一。它不仅能够确保多线程环境下的数据一致性,还可以帮助开发者管理线程间的同步问题。为了更好地理解和使用synchronized机制,深入剖析其背后的锁状态至关重要。

未锁定(Unlocked)

在Java虚拟机(JVM)中,对象的初始状态即为未锁定状态。这意味着没有任何线程持有该对象上的监视器锁。当第一个线程尝试获取对象锁时,将根据具体环境和配置,进入偏向、轻量级或重量级锁定模式。

示例代码

public class LockExample {
    public static void main(String[] args) {
        final Object obj = new Object();
        
        // 初始状态下,obj未被任何线程持有锁
    }
}

偏向锁定(Biased Locking)

当一个对象的锁状态首次转换时,如果JVM检测到该对象在长时间内只会由同一个线程访问,则会启用偏向锁定。在此模式下,虚拟机将直接为该线程提供偏向锁,这能极大减少无竞争情况下的同步开销。

开启偏向锁定

通过设置JVM启动参数,可以控制是否启用偏向锁定:

-XX:+UseBiasedLocking

轻量级锁定(Lightweight Locking)

当线程试图获取一个未被锁定的对象锁时,如果此时对象已经转换为偏向状态,并且另一个线程也尝试访问该对象,则会进入轻量级锁定阶段。在这一状态下,JVM会在栈帧中保存锁记录,从而避免直接使用操作系统级别的互斥操作。

示例代码

public class LockExample {
    public static void main(String[] args) throws InterruptedException {
        final Object obj = new Object();
        
        Thread t1 = new Thread(() -> {
            synchronized (obj) { // 轻量级锁定尝试
                System.out.println("Thread 1 acquired the lock");
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized (obj) {
                System.out.println("Thread 2 acquired the lock after thread 1 released it.");
            }
        });

        t1.start();
        t1.join(); // 等待t1执行完毕
        t2.start();
    }
}

重量级锁定(Heavyweight Locking)

当对象处于轻量级锁状态,但有多个线程竞争同一资源时,JVM可能会将锁升级为重量级锁。这种模式下,JVM会直接使用操作系统提供的互斥同步机制来保证线程安全。

示例代码

public class HeavyweightLockExample {
    private static final Object monitor = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (monitor) { // 可能导致重量级锁定
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (monitor) {
                System.out.println("Thread 2 acquired lock");
            }
        });
        
        t1.start();
        t2.start();
    }
}

总结与安全建议

在处理多线程并发控制时,了解synchronized关键字的底层机制十分关键。通过合理设计和使用锁策略,不仅可以提高程序性能,还能避免因不当同步而导致的问题。

重要提示: 当遇到频繁的竞争条件或死锁风险时,考虑使用更高级别的并发工具,如Java中的java.util.concurrent包下提供的各种线程安全的数据结构与工具类。这些组件通常内置了更加高效和灵活的锁定机制,并能提供更好的性能及稳定性保证。

以上内容提供了对synchronized关键字背后不同锁状态的深入理解。通过掌握这些概念和技术细节,开发者能够编写出更高效的多线程Java程序。