java volidate线程安全_03.(多线程与并发)面试题-02--Volidate的原理和指令重排序
線程棧(線程的工作內存)保存了線程運行時候變量值信息。當線程訪問某一個對象時候值的時候,首先通過對象的引用找到對應在堆內存的變量的值,然后把堆內存變量的具體值load到線程本地內存中,建立一個變量副本,之后線程就不再和對象在堆內存變量值有任何關系,而是直接修改副本變量的值,
在修改完之后的某一個時刻(線程退出之前),自動把線程變量本的值回寫到對象在堆中變量。這樣在堆中的對象的值就產生變化了。下面一幅圖副描述這寫交互
read and load 從主存復制變量到當前工作內存 use 代碼中使用值
assign ?改變共享變量值? store and write 用工作內存數據刷新主存相關內容
其中use and assign 可以多次出現
但是這一些操作并不是原子性,也就是 在read load之后,如果主內存count變量發生修改之后,線程工作內存中的值由于已經加載,不會產生對應的變化,所以計算出來的結果會和預期不一樣
總結:關鍵字上使用voildate之后,每一次use之前一定要read和load,這樣就保證了可見性,其他線程
指令重排序
總結:jvm會對指令執行的順序進行優化,這樣是為了提高執行的效率。但是在單線程的情況下指令的執行先后沒有關系,但是在多線程的情況下這些指令的執行順序就是對其他線程產生很大的影響。
很多介紹JVM并發的書或文章都會談到JVM為了優化性能,采用了指令重排序,但是對于什么是指令重排序,為什么重排序會優化性能卻很少有提及,其實道理很簡單,假設有這么兩個共享變量a和b:
private int a;
private int b;
在線程A中有兩條語句對這兩個共享變量進行賦值操作:
a = 1;
b = 2;
假設當線程A對a進行復制操作的時候發現這個變量在主內存已經被其它的線程加了訪問鎖,那么此時線程A怎么辦?等待釋放鎖?不,等待太浪費時間了,它會去嘗試進行b的賦值操作,b這時候沒被人占用,因此就會先為b賦值,再去為a賦值,那么執行的順序就變成了:
b = 2;
a = 1;
對于在同一個線程內,這樣的改變是不會對邏輯產生影響的,但是在多線程的情況下指令重排序會帶來問題,看下面這個情景:
在線程A中:
context = loadContext();
inited = true;
在線程B中:
while(!inited ){
sleep
}
doSomethingwithconfig(context);
假設A中發生了重排序:
inited = true;
context = loadContext();
那么B中很可能就會拿到一個尚未初始化或尚未初始化完成的context,從而引發程序錯誤。
想到有一條古老的原則很適合用在這個地方,那就是先要保證程序的正確然后再去優化性能。此處由于重排序產生的錯誤顯然要比重排序帶來的性能優化要重要的多。要解決重排序問題還是通過volatile關鍵字,volatile關鍵字能確保變量在線程中的操作不會被重排序而是按照代碼中規定的順序進行訪問,同時使用synchronized 關鍵字,里面也不會進行指令重排序。
總結
以上是生活随笔為你收集整理的java volidate线程安全_03.(多线程与并发)面试题-02--Volidate的原理和指令重排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 顶配15999元!七彩虹发布一体机G-O
- 下一篇: 如何用木板做桥_如何辨别使用的公园椅是否