synchronized仅提供排他性访问手段,线程安全取决于保护的共享状态及是否遗漏并发路径;实例方法锁this,静态方法锁class对象,同步块锁指定obj;volatile不保证原子性;锁优化由jvm自动处理,synchronized不可中断、无超时、不支持尝试获取。

synchronized 本身不“保证”线程安全,它只是提供了一种排他性访问临界区的手段;真正决定是否线程安全的,是你用它保护了哪些共享状态、是否漏掉了其他并发修改路径。
同步代码块和同步方法的锁对象到底是什么
关键不是语法,而是锁住谁:
- 实例方法上加
synchronized→ 锁的是当前实例(this) - 静态方法上加
synchronized→ 锁的是该类的Class对象(如MyClass.class) - 同步代码块
synchronized(obj) { ... }→ 锁的是obj引用的对象,必须确保所有相关线程用的是同一个obj
常见错误:多个线程各自 new 出不同实例,却只在实例方法上加 synchronized —— 这根本没互斥,因为锁对象各不相同。
为什么 volatile 不能替代 synchronized
volatile 只解决可见性和禁止指令重排序,不提供原子性。典型反例:
立即学习“Java免费学习笔记(深入)”;
private volatile int count = 0;
public void increment() {
count++; // 非原子操作:读-改-写三步,即使 count 是 volatile,仍可能丢失更新
}
而 synchronized 能保证整个 increment() 块内对 count 的读写是原子的(前提是所有修改都走同一把锁)。
锁升级和性能影响你其实很难控制
JVM 对 synchronized 做了锁优化(偏向锁 → 轻量级锁 → 重量级锁),但这些策略在 JDK 15+ 已默认禁用偏向锁,且实际行为高度依赖运行时竞争模式:
- 高竞争下,锁膨胀为重量级锁,会触发 OS 级线程挂起/唤醒,开销明显
- 锁粗化(JIT 自动合并相邻同步块)和锁消除(JIT 判定无共享逃逸时移除)是自动的,无法显式干预
- 不要为了“性能”而手动缩小同步块范围,除非你能确认缩小后逻辑依然正确——更常见的问题是同步范围过小导致数据不一致
最常被忽略的一点:synchronized 不可中断、不支持超时、无法尝试获取锁;如果业务需要这些能力,得换 ReentrantLock,而不是硬扛着改 synchronized 的用法。










