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

2022-09-21 20:21:13 字數 1206 閱讀 1304

原子性問題:

在乙個執行緒中,對乙個32的二進位制數進行賦值操作,當低16位的資料寫入後,發生了中斷,而此時又有乙個執行緒去讀取這個寫入的資料,必定得到的是乙個錯誤的資料。

在j**a中這種情況是不存在的,因為對基本資料型別的寫入和賦值保證了原子性(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.

在j**a中,volatile關鍵字可以保證可見性。

當乙個共享變數被volatile修飾時,它會保證修改的值會立即被更新到主存,當有其他執行緒需要讀取時,它會去記憶體中讀取新值。

另外,通過synchronized和lock也能夠保證可見性,synchronized和lock能保證同一時刻只有乙個執行緒獲取鎖然後執行同步**,並且在釋放鎖之前會將對變數的修改重新整理到主存當中。因此可以保證可見性。

有序性問題:

可見性 原子性和有序性問題

核心矛盾 這些年,我們的 cpu 記憶體 i o 裝置都在不斷迭代,不斷朝著更快的方向努力。但是,在這個快速發展的過程中,有乙個核心矛盾一直存在,就是這三者的速度差異。我形象的描述了一下這三者的速度上的差異 所謂天上一天地上一年 愛因斯坦的相對論是有合理解釋的 cpu和記憶體之間的速度差異就是cpu...

併發中的原子性問題

高階語言的程式中,一條程式 可能對應多個cpu指令,而原子性,即指乙個或多個操作在cpu中執行的過程中不被中斷,稱為 原子性 而因為原子性出現的bug的原因是因為執行緒切換,即指乙個變數的讀取操作在cpu中可能存在多個執行緒同一時間執行不同的順序,導致值的不對。怎麼解決這個問題呢?原子性問題的根源,...

併發程式設計 volatile的可見性

下面的 修改load函式中dosomething 0 傳入的值,從0到4,分別會出現能跳出迴圈和不能跳出迴圈兩種情況 0,什麼也不做,不能跳出迴圈 1,sleep,能跳出迴圈 2,使用system.out.println輸出,能跳出迴圈 3,等待10微秒,不能跳出迴圈 4,等待20微秒,能跳出迴圈 ...