執行緒安全是乙個非常燙手的山芋,因為即使合理運用了鎖,也不一定能保證執行緒安全,這是因為落後的編譯器無法滿足日益增長的併發需求,很多看似無錯的**在優化和併發面前產生了麻煩,可以看下面的**,
x = 0;
thread1 thread2
lock(); lock();
x++; x++;
unlock(); unlock();
上面的**看著是沒有任何問題的,對x的操作加鎖,可以保護多執行緒併發對x的操作,x的結果會是2,但實際是這樣嗎??
呵呵。。。顯然不是的,存在一種情況,編譯器為了提高x的訪問速度,將x放到某個暫存器裡,我們都知道每個執行緒都有不同的暫存器,那麼,下面的執行流程就會導致異常,
[thread1]讀取x的值到某個暫存器r[1](r[1]=0)。
[thread1]r[1]++(由於之後可能還要訪問x,因此thread1暫時不將r[1]寫回x)。
[thread2]讀取x的值到某個暫存器r[2](r[2]=0)。
[thread2]r[2]++(r[2]=1)。
[thread2]將r[2]寫回至x(x=1)。
[thread1](很久以後)將r[1]寫回至x(x=1)。
可見,在這種情況下,即使正常加鎖,也無法保證執行緒安全的。
還有乙個例子,請看,
x = y = 0;
thread1 thread2
x = 1; y = 1;
r1 = y; r2 = x;
這樣的**能保證 r1=r2=1嗎??猛的一看,沒問題,必然肯定100%至少r1和r2有乙個為1,可實際情況呢,呵呵,來分析一下嘍,
事實上,r1=r2=0確實可能存在,cpu為了提高效率,可能會將上下兩條互不相干的指令進行動態排程,導致thread1中x=1,r1=y順序調換,那麼thread2同樣也會存在這種情況,最終的結果可能r1和r2都為0,
那麼可以使用volatile關鍵字試圖阻止過度優化,volatile基本可以做到兩件事情:
(1)阻止編譯器為了提高速度將乙個變數快取到暫存器內而不寫回;
(2)阻止編譯器調整操作volatile變數的指令順序
還有乙個和序有關的問題,看下面的**,
volatile t* pinst = 0;
t* getinstance()
return pinst;
}
看著這段**覺得應該邏輯什麼的都是合理的,通過加鎖保證pinst被建立出來,並返回給呼叫方永遠指向乙個物件,事實上呢,真的如此嗎?我們結合new執行過程分析上述邏輯,
(1)申請記憶體;
(2)呼叫建構函式;
(3)將記憶體的位址賦值給pinst
這是整個過程,在這三步中,(2)(3)是可以調換順序的,也就是說,完全有可能出現這樣的情況:pinst的值已經不是null,但物件仍然沒有構造完畢。這時候如果出現另外乙個對getinstance的併發呼叫,此時第乙個if內的表示式pinst==null為false,所以這個呼叫會直接返回尚未構造完全的物件的位址(pinst)以提供給使用者使用。那麼程式這個時候會不會崩潰就取決於這個類的設計如何了。 有指令(barrier())可以阻止這種調換,但是還是強烈建議不要這麼設計**邏輯,為了併發安全,哈哈。
總結了這麼多,只有乙個結論,併發安全很重要一定要謹慎對待!!!對於上述存在情況,在實際開發過程中盡量不要存在這種使用方式。
多執行緒的併發問題?
1 描述 在乙個cpu上同時執行多個執行緒時,會存在多個執行緒競爭cpu資源的問題,但是有時候一段 是不允許打斷,或是出現死鎖的狀態。死鎖 多個執行緒出現了鎖巢狀,形成資源互相等待的狀態,使程式無法繼續執行。2 解決或避免死鎖狀態的方法 引入鎖物件 synchronized 同步 塊 在需要遵循原子...
筆記20200512 多執行緒 併發問題
案例 多個執行緒同時操作同乙個物件 買火車票的例子 多執行緒操作不安全咋處理呢?package com.chengguo.執行緒 多個執行緒同時操作同乙個物件 買火車票的例子 多執行緒操作不安全咋處理呢?public class demo 20200509002 thread implements ...
解決Lucene的多執行緒併發問題
建立indexwriter的方式 public class helloworld 2 在lucene程式中,成功以上面的方式建立indexwriter物件以後,會在索引庫中出現乙個鎖檔案,這個鎖檔案是當前這個indexwriter的鎖,如果呼叫indexwriter.close 關閉了鏈結,則將會把...