內存管理_原子性、可見性、有序性

原子性:即一個操做或者多個操做 要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行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並無被 初始化,就會致使程序出錯。

   從上面能夠看出,指令重排序不會影響單個線程的執行,可是會影響到線程併發執行的正確性。

  也就是說,要想併發程序正確地執行,必需要保證原子性、可見性以及有序性。只要有一個沒有被保證,就有可能會致使程序運行不正確。

相關文章
相關標籤/搜索