程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

synchronized 原理详解 synchronized怎么实现的

balukai 2024-12-26 11:40:13 文章精选 14 ℃

synchronized 是 Java 语言中一种用于实现线程同步的关键字,它通过监视器锁(Monitor Lock)来确保多个线程在同一时间只能有一个线程执行被 synchronized 关键字修饰的方法或代码块。synchronized 可以用于方法、静态方法和代码块,并能确保同一时间内对某个共享资源的操作是线程安全的。


synchronized 的使用

synchronized 关键字的使用主要有三种方式:

  1. 实例方法上的同步:持有的是对象锁(Object Lock)。
    public synchronized void instanceMethod() {
        // 线程安全的实例方法
    }
  1. 静态方法上的同步:持有的是类锁(Class Lock)。
    public static synchronized void staticMethod() {
        // 线程安全的静态方法
    }
  1. 代码块上的同步:可以对任意对象加锁。
    public void method() {
        synchronized (this) {
            // 线程安全的代码块
        }
    }

    public void methodWithCustomLock() {
        Object lockObject = new Object();
        synchronized (lockObject) {
            // 线程安全的代码块
        }
    }

synchronized 的工作原理

synchronized 关键字基于监视器锁(Monitor Lock)实现底层同步机制。具体来说,它依赖于 Java 对象头 中的 Monitor(监视器)来管理锁的状态。每个对象都有一个监视器,当一个线程进入同步方法或代码块时,它会获取该对象的监视器,而当退出时,它会释放监视器。

JVM 实现原理

synchronized 的真正实现依靠 JVM 完成。下面我将深入分析其工作机制。

对象头

在 HotSpot JVM 中,对象头包含以下两部分:

  • Mark Word:这是一个非常复杂的结构,用于存储对象的运行时数据,包括哈希码、GC状态、锁信息等。
  • Class Metadata Address:指向对象的类信息。

具体来说,当对象被 synchronized 修饰时,Mark Word 中的内容会变化以反映当前锁的状态。

锁的状态

锁的状态随着竞争程度的不同而升级,共有四种状态:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。

  1. 无锁状态(Unlocked):对象创建初始状态,Mark Word中存储的是对象的哈希码。
  2. 偏向锁状态(Biased Locking):偏向锁优化用于减少无竞争加锁的开销,通过将锁偏向第一个成功获取它的线程。
  3. 轻量级锁状态(Lightweight Locking):当偏向锁被另一个线程获取时,升级为轻量级锁。轻量级锁通过CAS(Compare-And-Swap)操作尝试加锁。
  4. 重量级锁状态(Heavyweight Locking):当轻量级锁竞争失败时,升级为重量级锁。重量级锁是通过操作系统的互斥量(Mutex)来实现的,会导致线程挂起和唤醒,代价较高。

锁升级过程如下图所示:

无锁状态 -> 偏向锁状态 -> 轻量级锁状态 -> 重量级锁状态

进入和退出同步块

在字节码层面,Java 的同步由 monitorenter 和 monitorexit 指令实现。每个 synchronized 代码块都会被编译成这些字节码指令。

  1. 进入同步块(monitorenter):线程尝试获取对象的监视器。如果成功,则进入同步块,否则线程将被阻塞。
  2. 退出同步块(monitorexit):线程释放对象的监视器。如果有其他线程在等待该监视器,则唤醒它们。

在 JVM 执行时,将会检查对象头中 Mark Word 的状态,并根据对象当前的锁状态进行相应的处理,例如获取锁、升级锁等。

示例代码和分析

下面是一个关于 synchronized 关键字的示例,展示了它如何用于实例方法、静态方法和代码块上的同步:

public class SynchronizedExample {
    private int count = 0;

    // 实例方法上的同步
    public synchronized void increment() {
        count++;
    }

    // 静态方法上的同步
    public static synchronized void staticIncrement() {
        // 静态变量的操作
    }

    // 代码块上的同步
    public void blockIncrement() {
        synchronized (this) {
            count++;
        }
    }

    // 多线程环境下测试
    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + example.count); // 期望值 2000
    }
}

总结


  • synchronized 是Java用于实现线程同步的关键字,确保在同一时间只有一个线程执行被修饰的方法或代码块。
  • 它基于监视器锁实现,同步的底层机制依赖于对象头中的 Mark Word。
  • 锁随着竞争程度的不同可以升级,从无锁状态到偏向锁、轻量级锁和重量级锁。
  • JVM 使用 monitorenter 和 monitorexit 指令实现同步的进入和退出过程。

通过理解synchronized的工作原理和实际应用,可以更好地编写线程安全的Java代码,也能有效地在需要时进行性能优化。希望这篇文章对你理解synchronized有所帮助。

最近发表
标签列表