保证多线程数据的可见性


程序的局部执行原理
每个CPU修改内存数据的步骤
一个CPU读数据,进行操作,另一个CPU写数据,就会产生数据不一致问题。
解决方案
地址总线 数据总线 控制总线,对总线进行加锁时,其他CPU无法进行使用,会造成效率低。
a、读操作:不做任何操作,把cache中的数据读到寄存器
b、写操作:发出信号,通知其他CPU,将该变量的CacheLine置为无效状态,其他CPU要访问这个变量的时候,只能从内存中获取。
Cache line 是cpu的一种实现机制,CPU的cache中会增加很多的Cache line。

volatile作用
让其他线程能够马上感知到某一线程对某个变量的修改
当一个线程对volatile修饰的变量进行修改时,会通过JMM控制向其他线程发出信号,映射到硬件中就是使Cache line置为无效
对共享变量的修改,其他线程马上能感知到,不能保证原子性。
多个原子性操作合在一起,就不存在原子性
public class Increa {
static volatile int i = 0;
public static void incre() {
i++;
}
public static void main(String[] args) {
for (int j = 0; j < 10; j++) {
new Thread(() -> {
for (int k = 0; k < 5; k++) {
incre();
}
}).start();
}
System.out.println(i);
}
}
//这里输出不是50,证明volatile不能保证原子性,i++操作为非原子操作
禁止重排序
重排序(编译阶段,指令优化阶段)
输入程序的代码顺序并不是实际执行的顺序
++重排序后对单线程没有影响,对多线程有影响++。
对于volatile修饰的变量:
使用hsdis汇编工具 反编译
java->class->JVM->ASM文件(汇编文件)
volatile int a=0;
代码映射到汇编语言
Lock:a

总结:java中volatile具有的功能:
public class ShutDowsnDemmo extends Thread{
private volatile boolean started=false;
@Override
public void run() {
while(started){
dowork();
}
}
public void shutdown(){
started=false;
}
}
public class Singleton {
private volatile static Singleton instance;
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class){
instance=new Singleton();
}
}
return instance;
}
}
Volatile只能修饰变量,synchronized只能修饰方法和语句块
synchronized可以保证原子性,Volatile不能保证原子性
都可以保证可见性,但实现原理不同
Volatile对变量加了lock,synchronized使用monitorEnter和monitorexit monitor JVM
Volatile能保证有序,synchronized可以保证有序性,但是代价(重量级)并发退化到串行
synchronized引起阻塞
Volatile不会引起阻塞
原文:https://www.cnblogs.com/wilsonsui/p/12790677.html