并发编程中的可见性-原子性-有序性

计算机的发展开始讲起:计算机开始的CPU 内存 I/O 这三者在使用时存在巨大的速度差异,为了使三者更好的交互,所以CPU增加了缓存,增加了多线程来分时复用I/O,编译优化使得缓存更好的利用。
3者中两者都很好的理解,但最后一点却无法理解?
查阅相关资料,得到解释如下:
同时这些技术解决技术问题的同时又引入了其它的问题。
1.缓存带来的可见性问题
2.线程切换带来的原子性问题
3.编译优化带来的有序性问题分别对以上问题进行产生原因进行分析。
为什么缓存能够带来可见性问题?
最早的CUP是直接和内存进行交互的,后来为平衡内存与CPU的速度,所以增加了缓存,后来为了提高效率,又变成了多核处理器,多核处理器,当不同线程在不同CPU上做同一份数据处理时,它们会把相同的数据加载到自己处理器的缓存中经行运算,由于它们对各自缓存的数据进行处理,所以导致最后的结果与预期不相符合,一个线程的数据处理对另一个线程是不可见的,所以称为可见性。
为什么线程切换会带来原子性?
因为CPU的原子性指的是CPU指令级别的,不是高级语言级别的。例如:count+=1;这一句很简单的话,但是代表了3个CPU指令级别如下:内存到寄存器,然后寄存器加1,最后写道内存中。前提:操作系统的任务切换是在任何一条CPU指令执行完。
为什么编译优化带来有序性?

Public class Singleton{
 Private static volatile Singleton singleton==null;
 Private  Singleton(){}
  Public static Singleton getInstance(){
  If(singleton==null){
  Synchronized( Singleton.class){
  If(singleton==null){
  Singleton=new singleton();
  }
  }
  }
  }
  Return singleton;
  }

如果没有 volatile修饰,代码会出现什么问题?
首先我们必须了解JAVA虚拟机new的原理,简化出原理如下:
分配内存,在内存上初始化对象,将该对象赋值给变量。
在编译优化优化后的流程:
分配内存,将该对象赋值给变量,最后在内存上初始化对象。
已知:操作系统的任务切换是在任何一条CPU指令执行完
当线程1在执行该方法后,当将该对象赋值给变量时,发生线程切换,线程B执行singleton==null时,singleton已经存在了,就会返回singleton对象,但实际上线程A还未执行在内存上初始化对象,线程B会触发空指针异常。

你可能感兴趣的:(并发编程中的可见性-原子性-有序性)