内存管理_原子性、可见性、有序性

原子性:即一个操做或者多个操做 要么所有执行而且执行的过程不会被任何因素打断,要么就都不执行java

好比存取款操做,存款和取款操做必须所有完成,或者所有不完成。多线程

 

可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其余线程可以当即看获得修改的值。并发

eg:优化

//Thread 1
int i = 0;
i = 10;
 
//Thread 2
j = i;

 

倘若执行Thread1的是CPU0,执行Thread2的是CPU1。由上面的分析可知,当Thread1执行 i =10这句时,会先把i的初始值加载到CPU0的Cache中,而后赋值为10,那么在CPU1的Cache当中i的值变为10了,却没有当即写入到RAM当中。此时Thread2执行 j = i,它会先去RAM读取i的值并加载到CPU1的Cache当中,注意此时内存当中i的值仍是0,那么就会使得j的值为0,而不是10.线程

这个就是著名的可见性问题。blog

 

有序性:即程序执行的顺序按照代码的前后顺序执行。排序

int i = 0;              
boolean flag = false;
i = 1;                //语句1  
flag = true;          //语句2

从代码顺序上看,语句1是在语句2前面的, 可是JVM在真正执行这段代码的时候可能会发生指令重排序(Instruction Reorder),语句1不必定在语句2前执行。通常来讲,处理器为了提升程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行前后顺序同代码中的顺序一致,可是它会保证程序最终执行结果和代码顺序执行的结果是一致的,这就是指令重排序。虽然处理器会对指令进行重排序,可是它会保证程序最终结果会和代码顺序执行结果相同,那么它靠什么保证的呢?再看下面一个例子:内存

int a = 10;    //语句1
int r = 2;    //语句2
a = a + 3;    //语句3
r = a*a;     //语句4

 那么可不多是这个执行顺序呢: 语句2   语句1    语句4   语句3it

  不可能,由于处理器在进行重排序时是会考虑指令之间的数据依赖性,若是一个指令Instruction 2必须用到Instruction 1的结果,那么处理器会保证Instruction 1会在Instruction 2以前执行。io

虽然重排序不会影响单个线程内程序执行的结果,可是多线程呢?下面看一个例子:

//线程1:
context = loadContext();   //语句1
inited = true;             //语句2
 
//线程2:
while(!inited ){
  sleep()
}
doSomethingwithconfig(context);

 上面代码中,因为语句1和语句2没有数据依赖性,所以可能会被重排序。假如发生了重排序,在线程1执行过程当中先执行语句2,而此是线程2会觉得初始 化工做已经完成,那么就会跳出while循环,去执行doSomethingwithconfig(context)方法,而此时context并无被 初始化,就会致使程序出错。

   从上面能够看出,指令重排序不会影响单个线程的执行,可是会影响到线程并发执行的正确性。

  也就是说,要想并发程序正确地执行,必需要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会致使程序运行不正确。

相关文章
相关标签/搜索