volatile提供的能力之可见性(一)
前言
java并发面临的三大问题,原子性、可见行、有序性; 1.原子性是指一块代码要不全部执行,并且在执行过程中不被干扰,要不都不执行。 2.可见性是指多线程对共享资源进行操作的时候,共享资源的修改能立刻对其他线程可见。 3.有序性是指java程序的顺序有可能不会按照行的顺序执行,编译器会代码的执行顺序优化,优化后的代码可能就不会按照代码顺序执行了,但是这种优化并不是对所有的代码都会生效的,只要遵循happen as if serial语意,就有可能对代码优化;这样看起来没有问题,但是针对一些特殊的场景就会有问题,比如懒汉模式的单例,就有可能出现问题,这个场景会在后面展示。
可见性能力
volatile可以提供共享资源的可见性,先通过一段代码的执行验证volatile的效果。
public class Test3 {
private boolean flag = true;
public void refresh(){
flag = false;
System.out.println(Thread.currentThread().getName() + "修改flag");
}
public void load(){
System.out.println(Thread.currentThread().getName() + "开始执行...");
while (flag){
}
System.out.println(Thread.currentThread().getName() + "跳出循环");
}
public static void main(String[] args) throws InterruptedException {
Test3 test3 = new Test3();
new Thread(new Runnable() {
@Override
public void run() {
test3.load();
}
},"threadA").start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
test3.refresh();
}
},"threadB").start();
}
}
上图虽然threadB修改了flag=false,但是线程A并没有感知到flag值的变化;结果可以看到,threadA线程没有跳出循环,一致运行着; 那将flag的值的添加volatile关键字之后,看下有什么效果?
public class Test3 {
private volatile boolean flag = true;
public void refresh(){
flag = false;
System.out.println(Thread.currentThread().getName() + "修改flag");
}
public void load(){
System.out.println(Thread.currentThread().getName() + "开始执行...");
while (flag){
}
System.out.println(Thread.currentThread().getName() + "跳出循环");
}
public static void main(String[] args) throws InterruptedException {
Test3 test3 = new Test3();
new Thread(new Runnable() {
@Override
public void run() {
test3.load();
}
},"threadA").start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
test3.refresh();
}
},"threadB").start();
}
}
运行结果: 从结果看:线程B修改flag之后,线程B跳出了循环,从这个例子就验证了volatile的可见性保证。
可见性的原理是如何实现的?
volatile在JMM语义上的保证
java内存模型规定一个变量从读取到写入的过程需要执行8步。 1、lock 2、read 作用于主内存的变量,将主内存的变量传输到线程的工作内存中 3、load 作用于工作内存的变量,将read的变量放入工作内存的变量副本中。 4、use 作用于工作内存的变量副本,把工作内存中的变量传递给执行引擎,每当虚拟机遇到变量值的字节码指令时将会执行这个操作。 5、assign 赋值操作,作用于工作内存的变量,虚拟机遇到对变量赋值字节码指令时执行次操作。 6、store 作用于工作内存的变量,把工作内存的值传输到主内存中,以便随后的write操作 7、write 作用于主内存的变量,它把store操作从工作内存传输的变量写入主内存的变量中 volatile总的原理,能够保证对共享变量的读取,每次都是从主内存中读取;能够保证对共享变量的写入,每次都是写成功后立即将新值刷新到主内存中。 8、unlock
使用volatile修改的变量的read、load、use操作和assign、store、write操作是连续的;即修改后立即将工作内存中的值刷新值主内存,使用时必须从主内存中刷新,由此保证volatile变量的可见性。
volatile在硬件层面上的实现
通过lock前缀指令,会锁定变量缓存行区域并写回主内存,这个操作成为缓存锁定,缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据,一个处理器的缓存回写到内存会导致其他处理器的缓存失效。
|