synchronized 的作用是什么 他的底层原理是什么?
synchronized 是 Java 中用于实现多线程同步的关键字,其核心作用是保证多线程在访问共享资源时的原子性、可见性和有序性,避免因线程并发执行导致的数据不一致或逻辑错误。
一、synchronized 的主要作用
- 原子性:确保被
synchronized修饰的代码块 / 方法在同一时间只能被一个线程执行,避免多线程同时操作共享资源导致的中间状态暴露(例如,避免两个线程同时修改同一变量时的覆盖问题)。 - 可见性:当线程释放
synchronized锁时,会将工作内存中修改的共享变量刷新到主内存;当线程获取锁时,会从主内存重新加载共享变量到工作内存,从而保证线程间数据的可见性。 - 有序性:通过锁机制隐式禁止指令重排序(被
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 锁:- 如果 Monitor 的
Owner为空(无线程持有锁),当前线程会成为Owner,并将 Monitor 的计数器(重入次数)设为 1。 - 如果当前线程已持有该 Monitor(重入),计数器加 1。
- 如果 Monitor 被其他线程持有,当前线程会进入 Monitor 的
Entry Set(入口集),处于BLOCKED状态,等待锁释放。
- 如果 Monitor 的
-
释放锁(解锁):线程退出
synchronized代码块时,会释放 Monitor 锁:- Monitor 的计数器减 1。
- 当计数器减为 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 并发编程中简单可靠的同步方案。
更多推荐


所有评论(0)