synchronized 是 Java 中的一个关键字,是 Java 内置的互斥与内存可见性机制。主要解决多个线程访问资源时的竞争问题。
三种使用方式
-
修饰实例方法(非静态)
-
锁对象: 当前实例 this(等价于 synchronized(this))
-
作用范围: 同一实例上的所有 synchronized 实例方法互斥;不同实例互不影响
-
适用场景: 保护实例字段(对象状态)
-
-
修饰静态方法
-
锁对象: 类对象 类名.class
-
作用范围: 所有实例共享一把锁;跨实例也互斥
-
适用场景: 保护静态字段/全局资源
-
-
同步代码块
-
锁对象: 自定义任意对象 synchronized(锁),粒度可控
-
作用范围: 仅包裹的临界区
-
适用场景: 需要缩小临界区提高并发,或按不同资源拆分多把锁
-
public class Counter {
private int count;
private static int global;
private final Object lock = new Object();
// 1) 实例方法锁:锁 this
public synchronized void incA() {
count++;
}
// 2) 静态方法锁:锁 Counter.class
public static synchronized void incGlobal() {
global++;
}
// 3) 同步代码块:锁自定义对象
public void incB() {
synchronized (lock) {
count++;
}
}
}
底层原理
实现方式
编译为字节码指令 monitorenter/monitorexit(方法级同步用 ACC_SYNCHRONIZED 标志)。
public class R {
public static void main(String[] args) {
new R().a();
}
private synchronized void a() {
System.out.println(Thread.currentThread().getId() + ": a()");
b();
}
private synchronized void b() {
System.out.println(Thread.currentThread().getId() + ": b()");
c();
}
private synchronized void c() {
System.out.println(Thread.currentThread().getId() + ": c()");
}
}
-
monitorenter此时计数器为 0,获取锁a()monitor计数器 += 1 = 1b()monitor计数器 += 1 = 2c()monitor计数器+= 1 = 3
-
monitorexitc()执行结束,monitor计数器 -= 1 = 2b()执行结束,monitor计数器 -= 1 = 1a()执行结束,monitor计数器 -= 1 = 0
-
释放锁
锁升级
-
对象头(Mark Word)存放锁状态/哈希等元数据,JVM 基于它实现多种锁形态并在运行时转换「锁升级」:
-
无锁 → 轻量级(自旋/偏向优化)→ 重量级(阻塞,OS 互斥量)
-
自旋锁与阻塞之间可能采用“自旋-再阻塞”策略以降低上下文切换成本
-
偏向锁在 JDK 15 起默认关闭,JDK 18 起被移除;现代 JDK 仍保留轻量/重量级与自旋等优化。
与 volatile 的关系
volatile提供可见性与有限的有序性,不提供互斥。synchronized同时提供“互斥 + 可见性/有序性”volatile只能作用于变量,synchronized可以修饰方法以及代码块 。