Java synchronized关键字详解:从入门到原理

Java synchronized 关键字详解:从入门到原理(2026最新视角)

synchronized 是 Java 中最基础、最重要的内置同步机制,也是面试和实际开发中最常被问到的并发原语之一。

一、synchronized 能解决什么问题?(最核心的三个性质)

性质含义synchronized 如何保证
原子性操作要么全部完成,要么都不发生进入同步块获得锁,退出同步块释放锁
可见性一个线程修改的结果对后续线程可见解锁前把工作内存 → 主内存(happens-before)
有序性禁止指令重排序(部分场景)进入/退出同步块建立 happens-before 关系

二、使用方式(从简单到进阶)

  1. 同步方法(最常用)
// 锁对象 = this(当前实例)
public synchronized void increment() {
    count++;
}

// 静态同步方法,锁对象 = Class 对象(字节码对象)
public static synchronized void staticMethod() { ... }
  1. 同步代码块(推荐:锁粒度更细)
// 推荐写法:锁粒度小
private final Object lock = new Object();

public void method() {
    // 业务前置代码...

    synchronized (lock) {   // 锁对象可以任意对象
        count++;
    }

    // 业务后置代码...
}
  1. 常见错误用法对比
// 错误示范1:不同对象锁不住
synchronized (new Object()) { ... }   // 每次都是新对象,没意义

// 错误示范2:String 常量池导致锁范围过大
synchronized ("lock") { ... }         // 危险!可能锁住其他使用相同字符串的地方

三、底层实现原理(重点!)

synchronized 的实现经历了巨大的优化演进:

JDK 1.6 之前:纯重量级锁(monitorenter / monitorexit → OS Mutex)

JDK 1.6 之后:引入锁升级优化(偏向 → 轻量级 → 重量级)

对象头 Mark Word 结构(64位 HotSpot)

锁状态Mark Word (64 bit) 主要内容锁标志位偏向标志位是否可偏向
无锁hashCode + 分代年龄01
偏向锁线程ID + epoch + 分代年龄011
轻量级锁指向线程栈中 LockRecord 的指针00
重量级锁指向 Monitor 对象的指针10
GC标记(特殊状态)11

锁升级全流程(非常重要!)

  1. 无锁 → 偏向锁(JDK 1.6 默认开启,-XX:-UseBiasedLocking 关闭)
  • 第一个线程访问时 → CAS 把线程 ID 写进 Mark Word
  • 后续同一个线程再次进入 → 只需比较线程 ID(零成本
  1. 偏向锁 → 轻量级锁(有第二个线程竞争)
  • 发生竞争 → 撤销偏向(revoke bias,需要安全点、全局 safepoint)
  • 当前线程在栈帧创建 Lock Record(Displaced Mark Word)
  • CAS 把 Mark Word 替换成指向 Lock Record 的指针
  1. 轻量级锁 → 重量级锁(自旋也抢不到)
  • 多个线程 CAS 失败 → 膨胀
  • 创建 Monitor 对象(重量级锁)
  • 没抢到的线程进入阻塞队列(park/unpark,由 OS 调度)
  • 重量级锁代价最高:用户态 ↔ 内核态切换

锁升级方向:无锁 → 偏向 → 轻量级 → 重量级(不可逆

JDK 最新动态(2024–2026)

  • JDK 15 开始逐步废弃偏向锁(-XX:-UseBiasedLocking 默认值在未来版本可能改变)
  • JDK 18+ 很多发行版已默认关闭偏向锁(因为现代应用竞争场景多,撤销偏向锁的成本反而更高)

四、synchronized 的内存语义(happens-before)

线程A                  线程B

synchronized(obj) {     // A 解锁
    x = 1;              ←─ happens-before ──→
    y = 2;                  看到 x=1, y=2
}                       synchronized(obj) { // B 加锁

规则

  • 解锁 → 后续对同一个锁的加锁 建立 happens-before
  • 这保证了可见性有序性(禁止部分重排序)

五、synchronized vs ReentrantLock(面试常问对比)

维度synchronizedReentrantLock
是否可重入
是否可中断不可(一直阻塞)可(lockInterruptibly())
是否可定时是(tryLock(timeout))
是否公平锁非公平可公平 / 非公平
是否支持条件变量不支持(只有 wait/notify)支持多个 Condition
代码可读性更简洁更复杂,需要 finally unlock
性能(现代 JDK)差距很小(偏向锁关闭后)略好(可精细控制)
使用推荐场景普通同步场景、代码简洁优先需要中断、超时、多个条件等待

六、总结:现代开发中 synchronized 的定位(2026视角)

  • 大多数普通业务:优先使用 synchronized(简单、安全、JVM 持续优化)
  • 高并发、低延迟:考虑 ReentrantLock / StampedLock / 读写锁 / 无锁并发容器
  • 极致性能:CAS + 自旋 + VarHandle / Atomic*(避开锁)
  • 面试最爱问的点:锁升级过程、Mark Word 变化、偏向锁撤销代价、内存语义

一句话总结:

synchronized 是“简单粗暴但经过十几年深度优化的悲观锁”,现代 JVM 已经让它在绝大多数场景下性能不再是瓶颈。

有想深入某个部分的(比如偏向锁撤销细节、字节码 monitorenter 实现、JOL 看对象头),可以继续问~

文章已创建 4665

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部