static int a=1,b=2,c=3;
int calc(){
int r1 = a * b;
int r2 = b * c;
return r1 + r2;
}
先分清核心:这段代码的重排有没有危害、需不需要修复
1. 单线程场景:完全无害,不用处理
as-if-serial 规则:单线程内无论 CPU / 编译器怎么重排 r1=a*b、r2=b*c 的执行顺序,最终 r1+r2 结果一定不变,业务无 bug。
2. 多线程并发场景才会出问题
存在多线程同时修改 a/b/c + 调用 calc() 读取的情况下,重排会导致读取到不同时刻的变量快照,计算结果错乱:示例时序:
- 线程 1 执行
r1 = a*b(读取此时 a=1,b=2) - 线程 2 修改
b=100 - CPU 重排先执行
r2 = b*c,读到新的 b=100,c=3 - 最终
r1=2、r2=300,混合新旧数据,结果不符合预期。
四种解决方案(按推荐优先级排序)
方案 1:a/b/c 全部加 volatile(简单通用,无锁)
volatile 变量读写自带内存屏障,禁止跨变量读写重排,保证每次读取都是主存最新值。
// 全部共享变量标记volatile
static volatile int a=1,b=2,c=3;
int calc(){
int r1 = a * b;
int r2 = b * c;
return r1 + r2;
}
优缺点
✅ 改动最小,无线程阻塞;❌ 每次读写都触发内存屏障,高并发大量计算有轻微性能损耗;适用:读多写少、并发竞争不激烈场景。
方案 2:同步锁 synchronized 包裹整个计算(强一致性,读写互斥)
临界区保证:同一时间只有一个线程执行读取计算,且锁的内存屏障禁止临界区内、外指令互相重排,读取期间变量不会被修改。
static int a=1,b=2,c=3;
// 方法加锁
synchronized int calc(){
int r1 = a * b;
int r2 = b * c;
return r1 + r2;
}
如果写变量的方法也加同一把锁,实现读写互斥,彻底杜绝混合新旧值问题。
优缺点
✅ 一致性最强,不会读到中间修改状态;❌ 并发会阻塞线程,大量并发计算吞吐量低;适用:读写竞争激烈、必须保证数据快照统一。
方案 3:先一次性拷贝所有共享变量到局部变量,再计算(性能最优,首选)
原理
先把 a/b/c 一次性读进线程私有局部变量,后续计算只操作局部变量,共享变量只读取一次,不存在中途被修改、重排打乱快照的问题。
static int a=1,b=2,c=3;
int calc(){
// 第一步:统一拷贝共享变量到局部,只读取一次主存
int va = a;
int vb = b;
int vc = c;
// 后续计算全部基于局部变量,不受共享变量修改、指令重排影响
int r1 = va * vb;
int r2 = vb * vc;
return r1 + r2;
}
核心优势
- 无 volatile、无锁,几乎无性能损耗;
- 无论 CPU 怎么重排内部乘法顺序,使用的都是同一瞬间的变量快照;
- 多线程修改全局变量不会干扰本次计算结果。生产中大量数值计算并发场景最优方案。
方案 4:使用 AtomicInteger 原子类封装 a/b/c(适合高频修改场景)
把普通 int 替换为原子类,get () 自带可见性,不会读到缓存过期值,规避重排带来的快照混乱:
static AtomicInteger a=new AtomicInteger(1);
static AtomicInteger b=new AtomicInteger(2);
static AtomicInteger c=new AtomicInteger(3);
int calc(){
int va = a.get();
int vb = b.get();
int vc = c.get();
int r1 = va * vb;
int r2 = vb * vc;
return r1 + r2;
}
适用场景
业务频繁并发修改 a/b/c,同时大量读取计算;原子类的 get 具备 volatile 读写语义。
错误方案避坑
只给单个变量加 volatile(比如只加 b)无法彻底解决:
static int a=1;
static volatile int b=2;
static int c=3;
a、c 无可见性保障,读取仍可能被重排、缓存到寄存器,依旧会混合新旧数据。
选型总结
- 追求性能、无锁优先 → 方案 3(局部变量拷贝快照);
- 代码极简、读多写少 → 方案 1(全部 volatile);
- 读写竞争激烈、需要强数据一致性 → 方案 2(synchronized 同步);
- 高频并发修改共享数值 → 方案 4 AtomicInteger。
4421

被折叠的 条评论
为什么被折叠?



