原子性:即一個操做或者多個操做 要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行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並無被 初始化,就會致使程序出錯。
從上面能夠看出,指令重排序不會影響單個線程的執行,可是會影響到線程併發執行的正確性。
也就是說,要想併發程序正確地執行,必需要保證原子性、可見性以及有序性。只要有一個沒有被保證,就有可能會致使程序運行不正確。