2025.6.6
多线程执行 int a = 1; int b = 2; int c = a + b;
的问题分析
你提供的代码:
int a = 1; // 指令1
int b = 2; // 指令2
int c = a + b; // 指令3
在 单线程 环境下,这段代码的执行顺序不会影响最终结果(c
始终是 3
)。
但在 多线程 环境下,如果多个线程同时执行这段代码,可能会遇到以下问题:
1. 多线程执行时的潜在问题
(1) 指令重排(Instruction Reordering)
- 编译器或 CPU 可能会优化指令顺序,例如:
- 先执行
int b = 2;
(指令2),再执行int a = 1;
(指令1)。 - 如果线程 A 执行
a = 1
但还没执行b = 2
,线程 B 读取a
和b
时可能得到a=1, b=0
(默认值),导致c
计算错误。
- 先执行
(2) 内存可见性问题(Visibility)
- 由于 CPU 缓存(Cache)的存在,一个线程修改的变量可能不会立即被其他线程看到。
- 例如:
- 线程 A 执行
a = 1
,但该值可能只写入 CPU 缓存,线程 B 仍然读到a=0
(初始值)。
- 线程 A 执行
(3) 竞态条件(Race Condition)
- 如果多个线程同时修改
a
或b
,最终c
的值可能不符合预期。 - 例如:
- 线程 A 执行
a = 1
,线程 B 同时执行a = 5
,最终a
的值可能是1
或5
,导致c
计算错误。
- 线程 A 执行
2. 如何让这段代码线程安全?
(1) 使用 synchronized
加锁
public class Example {
private int a, b, c;
public synchronized void calculate() {
a = 1; // 指令1
b = 2; // 指令2
c = a + b; // 指令3
}
}
- 优点:简单直接,保证原子性和可见性。
- 缺点:性能较差,多个线程不能并行执行。
(2) 使用 volatile
禁止指令重排
public class Example {
private volatile int a, b, c;
public void calculate() {
a = 1; // 指令1
b = 2; // 指令2
c = a + b; // 指令3
}
}
- 优点:比
synchronized
更轻量,防止指令重排。 - 缺点:不能保证原子性(如果多个线程同时调用
calculate()
,仍然可能出错)。
(3) 使用 AtomicInteger
(无锁优化)
import java.util.concurrent.atomic.AtomicInteger;
public class Example {
private final AtomicInteger a = new AtomicInteger();
private final AtomicInteger b = new AtomicInteger();
private final AtomicInteger c = new AtomicInteger();
public void calculate() {
a.set(1); // 指令1
b.set(2); // 指令2
c.set(a.get() + b.get()); // 指令3
}
}
- 优点:无锁,性能较好。
- 缺点:仍然不能保证
a
和b
的修改是原子的(如果多个线程同时调用calculate()
,c
可能计算错误)。
(4) 使用 ThreadLocal
(线程隔离)
public class Example {
private static final ThreadLocal<Integer> a = ThreadLocal.withInitial(() -> 0);
private static final ThreadLocal<Integer> b = ThreadLocal.withInitial(() -> 0);
private static final ThreadLocal<Integer> c = ThreadLocal.withInitial(() -> 0);
public void calculate() {
a.set(1); // 指令1
b.set(2); // 指令2
c.set(a.get() + b.get()); // 指令3
}
}
- 优点:每个线程有自己的变量副本,完全避免竞争。
- 缺点:适用于线程隔离的场景,不适用于共享变量。
3. 结论
方案 | 适用场景 | 线程安全保证 | 性能 |
---|---|---|---|
synchronized |
需要强一致性 | ✅ 原子性 + 可见性 | 较差 |
volatile |
仅需可见性 | ❌ 原子性,✅ 可见性 | 较好 |
AtomicInteger |
无锁优化 | ❌ 原子性(复合操作),✅ 可见性 | 较好 |
ThreadLocal |
线程隔离 | ✅ 无竞争 | 最佳 |
推荐方案
- 如果多个线程需要共享计算 → 用
synchronized
或ReentrantLock
保证原子性。 - 如果只是防止指令重排 → 用
volatile
。 - 如果每个线程独立计算 → 用
ThreadLocal
。
最终线程安全版本(推荐 synchronized
)
public class Example {
private int a, b, c;
public synchronized void calculate() {
a = 1;
b = 2;
c = a + b;
}
public synchronized int getC() {
return c;
}
}
这样,无论多少线程调用 calculate()
,c
的值始终是 3
。