併發編程中的原子性問題,可見性問題,有序性問題。

原子性問題:
在一個線程中,對一個32的二進制數進行賦值操做,當低16位的數據寫入後,發生了中斷,而此時又有一個線程去讀取這個寫入的數據,一定獲得的是一個錯誤的數據。
在java中這種狀況是不存在的,由於對基本數據類型的寫入和賦值保證了原子性(i=10)。但僅限制於對基本數據類型,而變量的賦值就不能保證,自增就是個很好的例子,
i++:
自增:先讀取數據,再加1,再寫入.
也就是說只有簡單的讀取,賦值纔是原子性。並且賦值必須是將數字賦給某個變量,變量之間的相互賦值不是原子性的,若是要實現更大範圍操做的原子性,能夠經過能夠經過synchronized和Lock來實現。
因爲synchronized和Lock可以保證任一時刻只有一個線程執行該代碼塊,那麼天然就不存在原子性問題了,從而保證了原子性。
 
可見性問題:
你們都知道,計算機在執行程序時,每條指令都是在CPU中執行的,而執行指令過程當中,勢必涉及到數據的讀取和寫入。因爲程序運行過程當中的臨時數據是存放在主存(物理內存)當中的,這時就存在一個問題,因爲CPU執行速度很快,而從內存讀取數據和向內存寫入數據的過程跟CPU執行指令的速度比起來要慢的多,所以若是任什麼時候候對數據的操做都要經過和內存的交互來進行,會大大下降指令執行的速度。所以在CPU裏面就有了高速緩存。
當程序運行過程當中,會將須要的數據從主內存複製一份給cpu的高速緩存,cpu進行計算時就能夠直接從高速緩存讀取和寫入數據,運算結束,把結果刷新到主存中。
線程一:int i=0; i=10;   線程二:j = i;
倘若執行線程1的是CPU1,執行線程2的是CPU2。由上面的分析可知,當線程1執行 i =10這句時,會先把i的初始值加載到CPU1的高速緩存中,而後賦值爲10,那麼在CPU1的高速緩存當中i的值變爲10了,卻沒有當即寫入到主存當中。                      
此時線程2執行 j = i,它會先去主存讀取i的值並加載到CPU2的緩存當中,注意此時內存當中i的值仍是0,那麼就會使得j的值爲0,而不是10.
在java中,volatile關鍵字能夠保證可見性。
  當一個共享變量被volatile修飾時,它會保證修改的值會當即被更新到主存,當有其餘線程須要讀取時,它會去內存中讀取新值。
另外,經過synchronized和Lock也可以保證可見性,synchronized和Lock能保證同一時刻只有一個線程獲取鎖而後執行同步代碼,而且在釋放鎖以前會將對變量的修改刷新到主存當中。所以能夠保證可見性。
 
有序性問題:
int a = 10;    //語句1 int r = 2;    //語句2 a = a + 3;    //語句3 r = a*a;     //語句4 這四條語句不必定按照順序執行。 通常來講,處理器爲了提升程序運行效率,可能會對輸入代碼進行優化,進行指令重排序。它不保證程序中各個語句的執行前後順序同代碼中的順序一致,可是它會保證程序最終執行結果和代碼順序執行的結果是一致的。 處理器在進行重排序時是會考慮指令之間的數據依賴性,若是一個指令Instruction 2必須用到Instruction 1的結果,那麼處理器會保證Instruction 1會在Instruction 2以前執行。 雖然重排序不會影響單個線程內程序執行的結果,可是多線程呢? 指令重排序不會影響單個線程的執行,可是會影響到線程併發執行的正確性。 1.volatile關鍵字的兩層語義   一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾以後,那麼就具有了兩層語義:   1)保證了不一樣線程對這個變量進行操做時的可見性,即一個線程修改了某個變量的值,這新值對其餘線程來講是當即可見的。   2)禁止進行指令重排序。   可是用volatile修飾以後就變得不同了:   第一:使用volatile關鍵字會強制將修改的值當即寫入主存;   第二:使用volatile關鍵字的話,當線程2進行修改時,會致使線程1的工做內存中緩存變量緩存行無效(反映到硬件層的話,就是CPU的L1或者L2緩存中對應的緩存行無效);   第三:因爲線程1的工做內存中緩存變量緩存行無效,因此線程1再次讀取變量的值時會去主存讀取。   那麼在線程2修改值時(固然這裏包括2個操做,修改線程2工做內存中的值,而後將修改後的值寫入內存),會使得線程1的工做內存中緩存變量的緩存行無效,而後線程1讀取時,發現本身的緩存行無效,它會等待緩存行對應的主存地址被更新以後,而後去對應的主存讀取最新的值。 那麼線程1讀取到的就是最新的正確的值。 volatile沒辦法保證對變量的操做的原子性。   在前面提到volatile關鍵字能禁止指令重排序,因此volatile能在必定程度上保證有序性。   volatile關鍵字禁止指令重排序有兩層意思:   1)當程序執行到volatile變量的讀操做或者寫操做時,在其前面的操做的更改確定所有已經進行,且結果已經對後面的操做可見;在其後面的操做確定尚未進行;   2)在進行指令優化時,不能將在對volatile變量訪問的語句放在其後面執行,也不能把volatile變量後面的語句放到其前面執行。 //x、y爲非volatile變量 //flag爲volatile變量 x = 2;        //語句1 y = 0;        //語句2 flag = true;  //語句3 x = 4;         //語句4 y = -1;       //語句5 因爲flag變量爲volatile變量,那麼在進行指令重排序的過程的時候,不會將語句3放到語句一、語句2前面,也不會講語句3放到語句四、語句5後面。可是要注意語句1和語句2的順序、語句4和語句5的順序是不做任何保證的。 而且volatile關鍵字能保證,執行到語句3時,語句1和語句2一定是執行完畢了的,且語句1和語句2的執行結果對語句三、語句四、語句5是可見的。
相關文章
相關標籤/搜索