Java 并发核心:synchronized 与 volatile 深度解析
Java 并发编程中的两个核心关键字:synchronized 和 volatile。它们都是为了解决多线程环境下的数据一致性问题,但在作用机制、保证的特性以及适用场景上有着本质的区别。
简单来说: synchronized 是一把'重量级的锁',它通过互斥访问来保证原子性、可见性和有序性。 volatile 是一个'轻量级的同步机制',它主要保证可见性和有序性,但不保证原子性。
1. synchronized 关键字详解
synchronized 是 Java 中最基础、最常用的同步机制,它通过获取和释放对象的'监视器锁'(Monitor Lock)来实现线程间的互斥访问。
1.1 作用与核心特性
-
互斥性 (Mutual Exclusion) 这是 synchronized 最核心的作用。它确保在同一时刻,只有一个线程能够执行被 synchronized 保护的代码块或方法。其他试图进入的线程会被阻塞,直到当前线程释放锁。
-
原子性 (Atomicity) 由于互斥性,被 synchronized 保护的代码块被视为一个不可分割的整体。线程要么执行完整个代码块,要么完全不执行,不会被其他线程打断。这保证了复合操作(如 i++)的原子性。
-
可见性 (Visibility) synchronized 不仅提供互斥,还保证了内存可见性。根据 Java 内存模型 (JMM) 的规定: 进入 synchronized 块时:线程会清空其工作内存中共享变量的副本,强制从主内存重新加载最新的值。 退出 synchronized 块时:线程会将其工作内存中对共享变量的修改强制刷新回主内存。 这样,一个线程在临界区内对变量的修改,对下一个进入该临界区的线程是立即可见的。
-
有序性 (Ordering) synchronized 通过'一个变量在同一时刻只允许一个线程对其进行 lock 操作'的规则,天然地禁止了指令重排序。在 synchronized 块内部,代码的执行顺序与程序的书写顺序一致。
1.2 使用方式
synchronized 可以修饰方法或代码块,锁定的对象不同,其作用范围也不同。
1.2.1 修饰实例方法 (非静态方法)
public class Counter {
private int count = 0;
// 锁定的是当前对象实例 (this)
public synchronized void increment() {
count++; // 这个操作是原子的
}
public synchronized int getCount() {
return count;
}
}
锁对象 当前对象实例 (this)。 作用范围 同一个对象实例的多个 synchronized 实例方法之间是互斥的。不同对象实例的 synchronized 方法可以并发执行。


