共享变量副本与 主内存共享变量的区别
  # 一、核心区别
| 维度 | 主内存共享变量 | 共享变量副本(工作内存) | 
|---|---|---|
| 存储位置 | JVM 堆内存中的全局共享区域(如实例变量、静态变量) | 线程私有的本地内存(CPU 缓存、寄存器等硬件优化空间) | 
| 数据可见性 | 所有线程可见,但需同步机制保证最新状态 | 线程私有,默认对其他线程不可见 | 
| 同步机制 | 通过 volatile、synchronized 或原子类强制刷新副本到主内存 |  默认无同步,线程直接操作副本,可能导致数据不一致 | 
# 二、Java 代码示例与说明
# 1. 可见性问题:线程副本未同步导致死循环
public class VisibilityDemo {
    private static boolean flag = false; // 主内存共享变量
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            System.out.println("Thread 1: Waiting...");
            while (!flag) { // 操作工作内存中的副本
                // 空循环
            }
            System.out.println("Thread 1: Detected flag change!");
        }).start();
        Thread.sleep(1000);
        
        new Thread(() -> {
            System.out.println("Thread 2: Changing flag...");
            flag = true; // 修改副本,但未及时刷新到主内存
        }).start();
    }
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
输出结果:
Thread 1: Waiting...
Thread 2: Changing flag...
(线程1永远无法退出循环)
 1
2
3
2
3
原因:线程2修改 flag 后未强制同步到主内存,线程1的工作内存副本仍为旧值 false。
# 2. 使用 volatile 解决可见性问题
 private static volatile boolean flag = false; // 强制读写直接操作主内存
 1
输出结果:
Thread 1: Waiting...
Thread 2: Changing flag...
Thread 1: Detected flag change!
 1
2
3
2
3
机制:volatile 通过内存屏障禁止指令重排序,并强制线程每次读写直接从主内存操作。
# 3. 使用 synchronized 保证可见性
 public class SynchronizedDemo {
    private static boolean flag = false;
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            synchronized (SynchronizedDemo.class) {
                System.out.println("Thread 1: Waiting...");
                while (!flag) {
                    // 空循环
                }
                System.out.println("Thread 1: Detected flag change!");
            }
        }).start();
        Thread.sleep(1000);
        new Thread(() -> {
            synchronized (SynchronizedDemo.class) {
                System.out.println("Thread 2: Changing flag...");
                flag = true;
            }
        }).start();
    }
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
输出结果:线程1正常退出循环。
 机制:synchronized 在加锁时清空工作内存并强制从主内存读取最新值,解锁前将修改刷新到主内存。
# 4. 复合操作的原子性问题
public class AtomicityDemo {
    private static volatile int count = 0;
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    count++; // 非原子操作(读取-修改-写入)
                }
            }).start();
        }
        Thread.sleep(2000);
        System.out.println("Final count: " + count); // 结果可能小于 10000
    }
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
原因:count++ 是三个操作的组合(读取主内存值 → 修改副本 → 写回主内存),即使使用 volatile 也无法保证原子性。
解决方案:
• 使用 AtomicInteger(基于 CAS 原子指令):
private static AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // 原子操作
 1
2
2
• 使用 synchronized 块:
synchronized (AtomicityDemo.class) {
    count++;
}
 1
2
3
2
3
# 三、总结
主内存与副本的本质差异:
• 主内存是全局唯一真实值,副本是线程私有临时缓存。
• 线程默认操作副本,需同步机制保证主内存与副本的一致性。同步工具选择:
•volatile:轻量级解决可见性和有序性,不保证原子性(如count++)。
•synchronized:保证可见性、原子性和有序性,但可能引发线程阻塞。
• 原子类(如AtomicInteger):无锁实现原子操作,适合高并发场景。
通过合理使用同步机制,可避免因副本与主内存不一致导致的多线程问题。
编辑  (opens new window)
  上次更新: 2025/06/13, 00:51:28