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 怎么管理线程?
- Monitor:可以理解为 JVM 的“锁管理员”。
它记录锁的持有线程和重入次数。锁空闲时,线程可以拿锁;被其他线程持有时,就等待;锁完全释放后,其他线程重新竞争。
锁为什么可重入?
可重入:同一个线程可以再次获取自己已经持有的锁。
JVM 发现持有者就是当前线程,就增加重入计数;每退出一次减一,归零才真正释放,避免嵌套调用时把自己堵住。
内存屏障是什么?
内存屏障:约束相关内存读写的顺序,配合同步规则保证可见性。
例如 A 先写
data = 10,再写 volatile 变量 ready = true;B 读到 ready == true 后再读 data,就能看到之前写入的 10(没有其他修改时)。编译器层保证不能把 A 的
data = 10 挪到 ready = true 后面;CPU层保证先让
data = 10 对其他 CPU 可见,再让 ready = true 可见。分享
