揭开JMM神秘面纱:内存可见性与volatile之谜
2023-10-03 06:01:31
前言
在多线程编程的世界中,Java内存模型(JMM)扮演着至关重要的角色。JMM定义了线程如何共享数据以及如何确保数据的一致性。其中,内存可见性和volatile是JMM的两大核心概念,理解它们对于编写可靠的多线程程序至关重要。
内存可见性
内存可见性是指一个线程对共享变量的修改能够立即被其他线程看到。换句话说,当一个线程修改了共享变量时,其他线程能够立即看到该变量的新值。
内存可见性示例
以下代码演示了内存可见性:
public class MemoryVisibility {
private static int sharedVariable = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
sharedVariable = 1;
});
Thread thread2 = new Thread(() -> {
while (sharedVariable == 0) {
// Busy waiting
}
System.out.println("Shared variable value: " + sharedVariable);
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
在这个示例中,线程1修改了共享变量sharedVariable的值,而线程2不断检查sharedVariable的值是否为0。如果内存可见性没有得到保证,线程2可能永远无法看到sharedVariable的新值,从而陷入死循环。然而,由于内存可见性得到了保证,线程2最终会看到sharedVariable的新值并打印它。
volatile关键字
volatile关键字可以保证内存可见性和原子性。
内存可见性
volatile关键字可以确保变量的修改能够立即被其他线程看到。这与内存可见性是一致的。当一个线程修改了一个volatile变量时,其他线程能够立即看到该变量的新值。
原子性
volatile关键字还可以保证变量的修改是原子的。这意味着变量的修改操作不会被中断,其他线程无法在修改操作进行时看到变量的中间状态。
volatile示例
以下代码演示了volatile关键字如何保证内存可见性和原子性:
public class VolatileExample {
private static volatile int sharedVariable = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
sharedVariable++;
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
sharedVariable++;
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Shared variable value: " + sharedVariable);
}
}
在这个示例中,线程1和线程2都对共享变量sharedVariable进行修改。由于sharedVariable是volatile的,因此线程1和线程2的修改操作都是原子的,并且其他线程能够立即看到这些修改。最终,sharedVariable的值等于线程1和线程2修改的总和。
总结
内存可见性和volatile关键字是JMM的两大核心概念,理解它们对于编写可靠的多线程程序至关重要。内存可见性确保了一个线程对共享变量的修改能够立即被其他线程看到,而volatile关键字可以保证内存可见性和原子性。通过熟练掌握内存可见性和volatile关键字,您可以编写出更加可靠和高效的多线程程序。