本文首发地址 https://www.dgjava.com/archives/javavolatile01
volatile在Java并发编程中常用于保持内存可见性和防止指令重排序
这句话很简单,看起来很好理解,但似乎又不好理解,所以我们干脆通过代码来说明。
/**
* 深入理解volatile关键字
*
* @Author: 地瓜(yangxw)
* @Date: 2020/4/8 12:55 AM
*/
public class VolatileTest {
final static int MXA = 5;
static int init_value = 0;
public static void main(String[] args) {
new Thread(() -> {
int local_value = init_value;
while (local_value < MXA) {
if (init_value != local_value) {
System.out.printf("init_value 更新为[%d]\n", init_value);
local_value = init_value;
}
}
}, "reader").start();
new Thread(() -> {
int local_value = init_value;
while (local_value < MXA) {
++local_value;
System.out.printf("init_value 变为[%d]\n", local_value);
init_value = local_value;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "update").start();
}
}
这是一段很简单的代码,main方法中启动两个线程。第一个线程比较 init_value 和 local_value 的值,如果不相等,打印最新的 init_value 的值,并更新。第二个线程则对 local_value 做自加操作,并赋值给 init_value 。理想状态下两个线程同时运行,应该输出类似 :
可以看到,第一个线程的输入代码没有如预期的那样实现,那就意味着在第一个线程的while循环中,local_value 首次赋值后,init_value 的值一直没变。实际上真的没有变吗?基于以上代码,我将第一个线程做一个改造,在循环中休眠200ms,并输出最新的local_value和init_value,代码如下:
new Thread(() -> {
int local_value = init_value;
while (local_value < MXA) {
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("local_value=[%d],init_value=[%d]\n", local_value, init_value);
if (init_value != local_value) {
System.out.printf("init_value 更新为[%d]\n", init_value);
local_value = init_value;
}
}
}, "reader").start();
看到这个结果,似乎有些明了。在第一个线程中,并不是init_value没有发生变化,只是线程执行太快,没能及时读取到init_value的最新值。
在一开始说到过,volatile在Java并发编程中常用于保持内存可见性和防止指令重排序。那我们不妨在init_value前加上volatile关键字,看看是否能在第一个线程中实时读取到init_value在内存中的最新值,我们把休眠的代码去掉,只保正常留输出。代码如下:
public class VolatileTest {
final static int MXA = 5;
volatile static int init_value = 0;
public static void main(String[] args) {
new Thread(() -> {
int local_value = init_value;
while (local_value < MXA) {
if (init_value != local_value) {
System.out.printf("init_value 更新为[%d]\n", init_value);
local_value = init_value;
}
}
}, "reader").start();
new Thread(() -> {
int local_value = init_value;
while (local_value < MXA) {
++local_value;
System.out.printf("init_value 变为[%d]\n", local_value);
init_value = local_value;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "update").start();
}
}
通过这个图,那么volatile在Java并发编程中常用于保持内存可见性就很好理解了。同时也可以结果,为什么volatile关键字在高并发场景下,为何那么好用了。
源码地址