Java 程序中怎么保證多線程的運行安全?


並發操作中的3大問題:原子性問題,可見性問題,有序性問題

原子性:一個或者多個操作在 CPU 執行的過程中不被中斷的特性
可見性:一個線程對共享變量的修改,另外一個線程能夠立刻看到
有序性:程序執行的順序按照代碼的先后順序執行

問題產生的原因

線程切換帶來的原子性問題

案列:
假設為一個32位的變量賦值包括兩個過程:為低16位賦值,為高16位賦值。可能發生一種情況:當將低16位數值寫入之后,突然被中斷,而此時又有一個線程去讀取該變量的值,那么讀取到的就是錯誤的數據。

緩存導致的可見性問題

案例:

//線程1執行的代碼
int i = 0;
i = 10;
 
//線程2執行的代碼
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。這就是可見性問題,線程1對變量i修改了之后,線程2沒有立即看到線程1修改的值。

編譯優化帶來的有序性問題

案列:

//線程1:
context = loadContext();   //語句1
inited = true;             //語句2
 
//線程2:
while(!inited ){
  sleep()
}
doSomethingwithconfig(context);

由於語句1和語句2沒有數據依賴性,因此可能會被重排序。假如發生了指令重排序,在線程1執行過程中先執行語句2,而此是線程2會以為初始化工作已經完成,那么就會跳出while循環,去執行doSomethingwithconfig(context)方法,而此時context並沒有被初始化,就會導致程序出錯。

解決辦法

JDK Atomic開頭的原子類、synchronized、LOCK,可以解決原子性問題
synchronized、volatile、LOCK,可以解決可見性問題(volatile不能解決原子性問題,可以解決有序性問題)
synchronized、LOCK、volatile、Happens-Before 規則可以解決有序性問題

參考文獻

https://www.cnblogs.com/dolphin0520/p/3920373.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM