Java内存模型和volatile
Java是一种并发语言,因此对于多线程并发的情况,必须要考虑更细致的问题。这些问题涉及到Java内存模型以及变量的可见性、有序性和原子性等等问题。其中,关于变量的可见性和原子性,Java中的volatile关键字有很重要的作用。
Java内存模型
Java内存模型(Java Memory Model,JMM)是一种抽象的规范,它定义了Java虚拟机如何和计算机内存进行交互。Java内存模型试图消除并发编程中存在的歧义和重排序问题。它定义了线程之间以及线程和主内存之间的数据传输和同步规则。
在Java内存模型中,共享变量存放在主内存中,而每个线程都有一份工作内存。工作内存是线程的私有数据区域,线程在执行操作时需要把变量从主内存拷贝到工作内存中。线程执行完操作后,将工作内存中的数据刷新回主内存。
volatile关键字
当多个线程访问同一个变量时,如果不进行同步操作,则会由于JVM的优化而导致线程之间的修改不可见。为了解决这个问题,Java SE 5引入了volatile变量,这个关键字保证了多线程之间修改变量时的可见性、有序性和原子性。
具体来说,当一个变量被声明为volatile变量时,读取这个变量和写入这个变量的操作都是对其它线程可见的。即使一个变量被同步机制保护,如果没有volatile修饰,其它线程仍然无法保证看到最新的值。
除了可见性外,volatile变量还保证了它们的操作是按照顺序执行的,即不会发生重排序。这对于一些需要顺序执行的操作非常重要,例如递增操作等等。
示例
示例1:不使用volatile关键字
public class Test {
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag has changed to " + flag);
}).start();
while(!flag) {
// do nothing
}
System.out.println("program end");
}
}
在这个示例中,我们创建了两个线程。其中一个线程会1秒后将flag修改为true,而另一个线程会不断检查flag是否为true,并打印输出。但是,由于flag并没有使用volatile关键字进行修饰,因此另一个线程无法感知到flag的修改,最终程序将陷入死循环。
示例2:使用volatile关键字
public class Test {
private static volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag has changed to " + flag);
}).start();
while(!flag) {
// do nothing
}
System.out.println("program end");
}
}
在这个示例中,我们只需要在flag的定义前加上volatile关键字,就可以保证在另一个线程中感知到flag的修改。运行程序后,生产输出如下
flag has changed to true
program end
说明在另一个线程中成功地感知到了flag的修改。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java高并发的volatile与Java内存模型详解 - Python技术站