Java内存模型与并发编程
# Java内存模型与并发编程
# 1. Java内存模型概述
# 1.1 什么是Java内存模型
Java内存模型(Java Memory Model,JMM)是Java虚拟机规范中定义的一个抽象概念,它描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。
# 1.2 主内存与工作内存
public class MemoryModelDemo {
private static int sharedVariable = 0;
private static boolean flag = false;
public static void demonstrateMemoryModel() {
// 线程1:写入数据
Thread writer = new Thread(() -> {
System.out.println("Writer: 开始写入数据");
sharedVariable = 42;
flag = true;
System.out.println("Writer: 数据写入完成");
}, "Writer-Thread");
// 线程2:读取数据
Thread reader = new Thread(() -> {
System.out.println("Reader: 开始读取数据");
while (!flag) {
// 等待flag变为true
Thread.yield();
}
System.out.println("Reader: 读取到的值 = " + sharedVariable);
}, "Reader-Thread");
reader.start();
try {
Thread.sleep(100); // 确保reader先启动
} catch (InterruptedException e) {
e.printStackTrace();
}
writer.start();
try {
writer.join();
reader.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
demonstrateMemoryModel();
}
}
# 2. 内存间交互操作
# 2.1 八种基本操作
Java内存模型定义了8种操作来完成主内存与工作内存之间的交互:
- lock(锁定):作用于主内存的变量
- unlock(解锁):作用于主内存的变量
- read(读取):作用于主内存的变量
- load(载入):作用于工作内存的变量
- use(使用):作用于工作内存的变量
- assign(赋值):作用于工作内存的变量
- store(存储):作用于工作内存的变量
- write(写入):作用于主内存的变量
public class MemoryOperationsDemo {
private static volatile int volatileVar = 0;
private static int normalVar = 0;
public static void demonstrateMemoryOperations() {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
// 对volatile变量的操作会立即同步到主内存
volatileVar = i;
normalVar = i;
System.out.println("Thread1: volatileVar = " + volatileVar +
", normalVar = " + normalVar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
int lastVolatileValue = -1;
int lastNormalValue = -1;
for (int i = 0; i < 10; i++) {
int currentVolatile = volatileVar;
int currentNormal = normalVar;
if (currentVolatile != lastVolatileValue ||
currentNormal != lastNormalValue) {
System.out.println("Thread2: 读取到 volatileVar = " + currentVolatile +
", normalVar = " + currentNormal);
lastVolatileValue = currentVolatile;
lastNormalValue = currentNormal;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
demonstrateMemoryOperations();
}
}
# 3. volatile关键字
# 3.1 volatile的特性
public class VolatileDemo {
private static volatile boolean stopFlag = false;
private static boolean normalFlag = false;
// 演示volatile的可见性
public static void demonstrateVisibility() {
System.out.println("=== 演示volatile可见性 ===");
// 使用volatile变量的线程
Thread volatileThread = new Thread(() -> {
int count = 0;
while (!stopFlag) {
count++;
if (count % 100000000 == 0) {
System.out.println("Volatile线程运行中... count = " + count);
}
}
System.out.println("Volatile线程结束,count = " + count);
}, "Volatile-Thread");
// 使用普通变量的线程
Thread normalThread = new Thread(() -> {
int count = 0;
while (!normalFlag) {
count++;
if (count % 100000000 == 0) {
System.out.println("Normal线程运行中... count = " + count);
}
}
System.out.println("Normal线程结束,count = " + count);
}, "Normal-Thread");
volatileThread.start();
normalThread.start();
try {
Thread.sleep(1000);
System.out.println("主线程:设置停止标志");
stopFlag = true;
normalFlag = true;
// 等待线程结束
volatileThread.join(2000);
if (normalThread.isAlive()) {
System.out.println("Normal线程可能因为可见性问题未能及时停止");
normalThread.interrupt();
}
normalThread.join(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 演示volatile禁止指令重排序
private static volatile boolean initialized = false;
private static int value = 0;
public static void demonstrateOrdering() {
System.out.println("\n=== 演示volatile禁止重排序 ===");
Thread initThread = new Thread(() -> {
System.out.println("初始化线程:开始初始化");
value = 42; // 1. 设置值
initialized = true; // 2. 设置初始化标志(volatile写)
System.out.println("初始化线程:初始化完成");
}, "Init-Thread");
Thread useThread = new Thread(() -> {
System.out.println("使用线程:等待初始化完成");
while (!initialized) { // volatile读
Thread.yield();
}
// 由于volatile的happens-before规则,这里一定能看到value=42
System.out.println("使用线程:读取到value = " + value);
}, "Use-Thread");
useThread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
initThread.start();
try {
initThread.join();
useThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
demonstrateVisibility();
demonstrateOrdering();
}
}
# 3.2 volatile的使用场景
import java.util.concurrent.atomic.AtomicInteger;
public class VolatileUseCases {
// 场景1:状态标志
private static volatile boolean shutdown = false;
public static void statusFlagExample() {
System.out.println("=== 状态标志示例 ===");
Thread worker = new Thread(() -> {
while (!shutdown) {
// 执行工作
System.out.println("工作线程正在运行...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
break;
}
}
System.out.println("工作线程已停止");
});
worker.start();
try {
Thread.sleep(2000);
System.out.println("主线程:发送停止信号");
shutdown = true;
worker.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 场景2:一次性安全发布
private static volatile Resource resource;
static class Resource {
private final String data;
public Resource(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
public static void safePublicationExample() {
System.out.println("\n=== 安全发布示例 ===");
Thread publisher = new Thread(() -> {
System.out.println("发布者:创建资源");
Resource newResource = new Resource("重要数据");
resource = newResource; // volatile写,确保安全发布
System.out.println("发布者:资源发布完成");
});
Thread consumer = new Thread(() -> {
System.out.println("消费者:等待资源");
while (resource == null) {
Thread.yield();
}
System.out.println("消费者:获取到资源 - " + resource.getData());
});
consumer.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
publisher.start();
try {
publisher.join();
consumer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 场景3:独立观察(注意:volatile不保证原子性)
private static volatile int counter = 0;
private static AtomicInteger atomicCounter = new AtomicInteger(0);
public static void atomicityExample() {
System.out.println("\n=== 原子性示例(volatile vs atomic) ===");
int threadCount = 5;
int incrementsPerThread = 1000;
Thread[] threads = new Thread[threadCount];
// 使用volatile变量(不保证原子性)
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < incrementsPerThread; j++) {
counter++; // 非原子操作
atomicCounter.incrementAndGet(); // 原子操作
}
});
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int expected = threadCount * incrementsPerThread;
System.out.println("期望值: " + expected);
System.out.println("volatile counter: " + counter +
" (可能小于期望值,因为++不是原子操作)");
System.out.println("atomic counter: " + atomicCounter.get() +
" (应该等于期望值)");
}
public static void main(String[] args) {
statusFlagExample();
safePublicationExample();
atomicityExample();
}
}
# 4. happens-before规则
# 4.1 happens-before关系
import java.util.concurrent.locks.ReentrantLock;
public class HappensBeforeDemo {
private static int x = 0;
private static int y = 0;
private static volatile boolean flag = false;
private static final ReentrantLock lock = new ReentrantLock();
// 规则1:程序次序规则
public static void programOrderRule() {
System.out.println("=== 程序次序规则 ===");
Thread thread = new Thread(() -> {
x = 1; // 操作A
y = 2; // 操作B
// 根据程序次序规则,A happens-before B
System.out.println("x = " + x + ", y = " + y);
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 规则2:volatile变量规则
public static void volatileVariableRule() {
System.out.println("\n=== volatile变量规则 ===");
Thread writer = new Thread(() -> {
x = 42;
flag = true; // volatile写
System.out.println("Writer: 设置x=42, flag=true");
});
Thread reader = new Thread(() -> {
if (flag) { // volatile读
// 由于volatile规则,writer中的x=42 happens-before 这里的读取
System.out.println("Reader: flag为true,x = " + x);
}
});
writer.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
reader.start();
try {
writer.join();
reader.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 规则3:锁规则
public static void lockRule() {
System.out.println("\n=== 锁规则 ===");
Thread thread1 = new Thread(() -> {
lock.lock();
try {
x = 100;
System.out.println("Thread1: 在锁内设置x=100");
} finally {
lock.unlock(); // 解锁操作
}
});
Thread thread2 = new Thread(() -> {
lock.lock(); // 加锁操作
try {
// 由于锁规则,thread1的解锁 happens-before thread2的加锁
// 因此thread2能看到thread1对x的修改
System.out.println("Thread2: 在锁内读取x = " + x);
} finally {
lock.unlock();
}
});
thread1.start();
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
try {
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 规则4:线程启动规则
public static void threadStartRule() {
System.out.println("\n=== 线程启动规则 ===");
x = 200; // 主线程中的操作
Thread childThread = new Thread(() -> {
// 由于线程启动规则,主线程中start()之前的操作 happens-before 子线程中的操作
System.out.println("子线程: 读取到x = " + x);
});
childThread.start(); // 启动子线程
try {
childThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 规则5:线程终止规则
public static void threadTerminationRule() {
System.out.println("\n=== 线程终止规则 ===");
Thread childThread = new Thread(() -> {
y = 300;
System.out.println("子线程: 设置y=300");
});
childThread.start();
try {
childThread.join(); // 等待子线程结束
// 由于线程终止规则,子线程中的操作 happens-before join()的返回
System.out.println("主线程: 子线程结束后读取y = " + y);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
programOrderRule();
volatileVariableRule();
lockRule();
threadStartRule();
threadTerminationRule();
}
}
# 5. 内存屏障
# 5.1 内存屏障类型
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class MemoryBarrierDemo {
private static Unsafe unsafe;
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
private static volatile int volatileVar = 0;
private static int normalVar1 = 0;
private static int normalVar2 = 0;
// 演示内存屏障的作用
public static void demonstrateMemoryBarrier() {
System.out.println("=== 内存屏障演示 ===");
Thread writer = new Thread(() -> {
// 写入普通变量
normalVar1 = 1;
normalVar2 = 2;
// 插入StoreStore屏障(通过volatile写实现)
volatileVar = 3;
System.out.println("Writer: 完成写入操作");
});
Thread reader = new Thread(() -> {
// 读取volatile变量(插入LoadLoad和LoadStore屏障)
int vol = volatileVar;
if (vol == 3) {
// 由于内存屏障,这里能看到之前的写入
System.out.println("Reader: volatileVar=" + vol +
", normalVar1=" + normalVar1 +
", normalVar2=" + normalVar2);
}
});
writer.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
reader.start();
try {
writer.join();
reader.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 使用Unsafe直接操作内存屏障(仅用于演示,实际开发不推荐)
public static void unsafeMemoryBarrier() {
if (unsafe == null) {
System.out.println("Unsafe不可用,跳过演示");
return;
}
System.out.println("\n=== Unsafe内存屏障演示 ===");
Thread thread = new Thread(() -> {
normalVar1 = 10;
// 插入StoreStore屏障
unsafe.storeFence();
normalVar2 = 20;
// 插入StoreLoad屏障
unsafe.fullFence();
System.out.println("使用Unsafe插入内存屏障完成");
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
demonstrateMemoryBarrier();
unsafeMemoryBarrier();
}
}
# 6. 双重检查锁定
# 6.1 单例模式的内存模型问题
public class DoubleCheckedLockingDemo {
// 错误的双重检查锁定实现
static class BrokenSingleton {
private static BrokenSingleton instance;
private BrokenSingleton() {
// 模拟复杂的初始化过程
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 存在问题的实现
public static BrokenSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (BrokenSingleton.class) {
if (instance == null) { // 第二次检查
instance = new BrokenSingleton(); // 问题所在
}
}
}
return instance;
}
}
// 正确的双重检查锁定实现
static class CorrectSingleton {
private static volatile CorrectSingleton instance;
private CorrectSingleton() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static CorrectSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (CorrectSingleton.class) {
if (instance == null) { // 第二次检查
instance = new CorrectSingleton(); // volatile确保正确发布
}
}
}
return instance;
}
}
// 基于类初始化的线程安全单例
static class InitializationOnDemandSingleton {
private InitializationOnDemandSingleton() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class InstanceHolder {
static final InitializationOnDemandSingleton INSTANCE =
new InitializationOnDemandSingleton();
}
public static InitializationOnDemandSingleton getInstance() {
return InstanceHolder.INSTANCE;
}
}
public static void testSingletonImplementations() {
System.out.println("=== 测试单例模式实现 ===");
// 测试正确的双重检查锁定
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
CorrectSingleton singleton = CorrectSingleton.getInstance();
System.out.println("CorrectSingleton实例: " +
System.identityHashCode(singleton));
});
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println();
// 测试基于类初始化的单例
for (int i = 0; i < 5; i++) {
new Thread(() -> {
InitializationOnDemandSingleton singleton =
InitializationOnDemandSingleton.getInstance();
System.out.println("InitializationOnDemandSingleton实例: " +
System.identityHashCode(singleton));
}).start();
}
}
public static void main(String[] args) {
testSingletonImplementations();
}
}
# 7. 总结
# 7.1 关键概念
- Java内存模型:定义了线程间如何通过内存进行交互的规范
- 主内存与工作内存:JMM的抽象概念,用于描述变量的存储和访问
- volatile关键字:保证可见性和有序性,但不保证原子性
- happens-before规则:定义了操作间的偏序关系
- 内存屏障:防止指令重排序的硬件或软件机制
# 7.2 最佳实践
- 正确使用volatile:用于状态标志、安全发布等场景
- 理解happens-before:确保线程间的可见性
- 避免双重检查锁定陷阱:使用volatile或其他线程安全方式
- 选择合适的同步机制:根据需求选择synchronized、volatile、Lock等
- 关注内存可见性:在多线程环境中确保数据的正确同步
# 7.3 常见问题
- 可见性问题:一个线程的修改对其他线程不可见
- 重排序问题:编译器或处理器的优化导致执行顺序改变
- 原子性问题:复合操作不是原子的
- 双重检查锁定问题:不正确的单例实现可能导致问题
理解Java内存模型是编写正确并发程序的基础,它帮助我们理解多线程程序的行为,避免常见的并发问题。