synchronized 是 Java 中用于实现多线程同步的关键字,其核心作用是保证多线程在访问共享资源时的原子性、可见性和有序性,避免因线程并发执行导致的数据不一致或逻辑错误。

一、synchronized 的主要作用

  1. 原子性:确保被 synchronized 修饰的代码块 / 方法在同一时间只能被一个线程执行,避免多线程同时操作共享资源导致的中间状态暴露(例如,避免两个线程同时修改同一变量时的覆盖问题)。
  2. 可见性:当线程释放 synchronized 锁时,会将工作内存中修改的共享变量刷新到主内存;当线程获取锁时,会从主内存重新加载共享变量到工作内存,从而保证线程间数据的可见性。
  3. 有序性:通过锁机制隐式禁止指令重排序(被 synchronized 修饰的代码块会被视为一个整体,内部指令重排序不会影响外部逻辑),避免多线程环境下因指令重排导致的执行顺序混乱。

二、synchronized 的底层原理

synchronized 的实现依赖于 JVM 中的监视器锁(Monitor) 机制,而 Monitor 的底层则与操作系统的互斥锁(Mutex) 相关联。其核心逻辑可从 “锁的存储形式” 和 “加锁 / 解锁流程” 两方面解析:

1. 锁的存储形式:对象头中的 Mark Word

Java 中每个对象都有一个对象头(Object Header),其中的 Mark Word 字段是实现 synchronized 锁的关键,它存储了对象的锁状态信息,包括:

3. 锁的升级过程(优化机制)

为了减少锁的性能开销,JVM 对 synchronized 实现了锁升级策略,从低开销到高开销逐步升级:

  • 无锁状态
  • 偏向锁状态(只允许一个线程获取,减少无竞争时的锁开销)
  • 轻量级锁状态(多线程交替执行,无激烈竞争)
  • 重量级锁状态(多线程激烈竞争,依赖操作系统互斥锁)
  • 2. 加锁与解锁的底层流程(以重量级锁为例)

    当 synchronized 修饰代码块或方法时,JVM 会通过 Monitor 实现锁的获取与释放,流程如下:

  • 获取锁(加锁):线程执行到 synchronized 代码块时,会尝试获取对象关联的 Monitor 锁:

    1. 如果 Monitor 的 Owner 为空(无线程持有锁),当前线程会成为 Owner,并将 Monitor 的计数器(重入次数)设为 1。
    2. 如果当前线程已持有该 Monitor(重入),计数器加 1。
    3. 如果 Monitor 被其他线程持有,当前线程会进入 Monitor 的 Entry Set(入口集),处于 BLOCKED 状态,等待锁释放。
  • 释放锁(解锁):线程退出 synchronized 代码块时,会释放 Monitor 锁:

    1. Monitor 的计数器减 1。
    2. 当计数器减为 0 时,Owner 置空,释放锁。此时,Entry Set 中等待的线程会被唤醒,重新竞争锁。
  • 无锁:对象刚创建时,处于无锁状态,Mark Word 存储对象哈希码等信息。
  • 偏向锁:当只有一个线程多次获取锁时,Mark Word 记录该线程 ID,后续该线程可直接获取锁(无需竞争),减少 CAS 操作开销。
  • 轻量级锁:当有其他线程竞争锁时,偏向锁升级为轻量级锁。线程通过 CAS 操作在栈帧中创建锁记录(Lock Record),并尝试将 Mark Word 指向该记录,若成功则获取锁,失败则自旋重试(避免立即进入阻塞)。
  • 重量级锁:当自旋次数达到阈值(或线程数过多),轻量级锁升级为重量级锁。此时依赖操作系统的互斥锁(Mutex)实现,线程会进入阻塞状态(BLOCKED),开销较大,但适合激烈竞争场景。
  • 作用位置: 对象锁:synchronized 修饰对象或者方法,默认锁对象为this,当前实例对象
  • 方法锁:synchronized 修饰普通方法,锁对象默认为this
  • 同步代码块锁:synchronized修饰代码块,自己指定锁对象
  • 类锁:synchronize 修饰静态的方法或指定锁对象为Class对象 每一个对象在同一时间只与一个monitor(锁)相关联,而一个monitor在同一时间只能被一个 线程获得。

三、用 synchronized 解决线程安全问题

修饰方法(锁当前对象)
public class SynchronizedMethodExample {
    private int count = 0;

    // synchronized 修饰实例方法(锁对象为 this)
    public synchronized void increment() {
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedMethodExample example = new SynchronizedMethodExample();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                example.increment();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                example.increment();
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        // 结果一定是 20000(线程安全)
        System.out.println("最终计数:" + example.count);
    }
}
修饰静态方法(锁类对象)
public class SynchronizedStaticExample {
    private static int count = 0;

    // synchronized 修饰静态方法(锁对象为类本身:SynchronizedStaticExample.class)
    public static synchronized void increment() {
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                increment();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                increment();
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("最终计数:" + count); // 20000(线程安全)
    }
}
修饰代码块(锁指定对象)
public class SynchronizedBlockExample {
    private int count = 0;
    private final Object lock = new Object(); // 自定义锁对象

    public void increment() {
        // synchronized 修饰代码块(锁对象为 lock)
        synchronized (lock) {
            count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedBlockExample example = new SynchronizedBlockExample();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                example.increment();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                example.increment();
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("最终计数:" + example.count); // 20000(线程安全)
    }
}

synchronized 与 wait/notify 协作示例

结合 wait() 和 notify() 实现线程间通信:

public class WaitNotifyExample {
    private int num = 0;
    private final Object lock = new Object();

    // 生产者:增加数字并通知消费者
    public void produce() throws InterruptedException {
        synchronized (lock) {
            while (num >= 5) { // 避免虚假唤醒,用 while 循环判断
                System.out.println("库存已满,生产者等待...");
                lock.wait(); // 释放锁,进入等待状态
            }
            num++;
            System.out.println("生产者生产,当前库存:" + num);
            lock.notify(); // 唤醒等待的消费者
        }
    }

    // 消费者:减少数字并通知生产者
    public void consume() throws InterruptedException {
        synchronized (lock) {
            while (num <= 0) { // 避免虚假唤醒
                System.out.println("库存为空,消费者等待...");
                lock.wait(); // 释放锁,进入等待状态
            }
            num--;
            System.out.println("消费者消费,当前库存:" + num);
            lock.notify(); // 唤醒等待的生产者
        }
    }

    public static void main(String[] args) {
        WaitNotifyExample example = new WaitNotifyExample();

        // 启动生产者线程
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    example.produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // 启动消费者线程
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    example.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

说明

  • synchronized 保证了对 num 的原子操作和可见性。
  • wait() 释放锁并让线程等待,notify() 唤醒等待线程,两者必须在 synchronized 块中调用(依赖锁的 Monitor 机制)。
总结:

synchronized 是 Java 内置的多线程同步关键字,核心是通过 Monitor 机制保证共享资源访问的原子性、可见性和有序性,解决并发数据不一致问题。其底层依赖对象头 Mark Word 存储锁状态,采用 “无锁→偏向锁→轻量级锁→重量级锁” 的动态升级策略,平衡性能与竞争需求;加解锁围绕 Monitor 的 Owner 身份和计数器实现,支持重入且自动管理锁的获取与释放。使用场景包括修饰实例方法(锁当前实例)、静态方法(锁类对象)、代码块(自定义锁对象),还可与 wait()/notify() 配合实现线程间通信,是 Java 并发编程中简单可靠的同步方案。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐