Lazy loaded image
介绍下synchronized 和 volatile
Words 696Read Time≈ 2 min
2026-10-11
type
Post
status
Published
date
Oct 11, 2026
slug
synchronized
summary
tags
技术探索
category
手写
icon
password
synchronized 用锁保护一段代码;volatile 保证变量的跨线程可见性,并约束相关重排序。
对比
synchronized
volatile
用在哪里
方法、代码块
字段
互斥
同一把锁下,一次只有一个线程执行
不互斥,多个线程可同时操作
可见性
释放锁前的修改,对随后获取同一锁的线程可见
写入对后续读取可见
多步操作的原子性
使用同一把锁保护时,可以保证
不保证,例如 count++
使用场景
计数、扣库存等需要保护完整操作的场景
停止标记、状态通知等简单读写

为什么 volatile 不能保证 count++ 安全?

count++ 实际有三步:读值 → 加一 → 写回。
两个线程可能都读到 0,然后都写回 1,最终少加了一次。用同一把锁包住这三步,两个线程就会依次执行,结果为 2。
所以:只需要通知状态,用 volatile;需要保护多步操作,用 synchronized。

synchronized 和 volatile底层实现:

  • synchronized:基于对象的监视器锁(Monitor);同步代码块通过 monitorenter 获取锁、monitorexit 释放锁。
  • volatile:通过编译器限制相关重排序,并根据 CPU 架构使用必要的内存屏障,实现可见性和有序性。

Monitor 怎么管理线程?

  1. Monitor:可以理解为 JVM 的“锁管理员”。
    1. 它记录锁的持有线程和重入次数。锁空闲时,线程可以拿锁;被其他线程持有时,就等待;锁完全释放后,其他线程重新竞争。

锁为什么可重入?

可重入:同一个线程可以再次获取自己已经持有的锁。
JVM 发现持有者就是当前线程,就增加重入计数;每退出一次减一,归零才真正释放,避免嵌套调用时把自己堵住。

内存屏障是什么?

内存屏障:约束相关内存读写的顺序,配合同步规则保证可见性。
例如 A 先写 data = 10,再写 volatile 变量 ready = true;B 读到 ready == true 后再读 data,就能看到之前写入的 10(没有其他修改时)。
编译器层保证不能把 A 的 data = 10 挪到 ready = true 后面;
CPU层保证先让 data = 10 对其他 CPU 可见,再让 ready = true 可见。
回到首页