cpu缓冲区大小怎么设置_JAVA高薪面试必备知识点Volatile底层原理探究CPU在作怪
基礎知識回顧
還是那句話,無論語言再怎么牛,其都是對底層計算機指令的封裝。
計算機CPU執行指令的時候是非常快的,如果每執行一個指令都從內存中取數據的話,那會非常慢,嚴重影響CPU的執行速度,所以每個CPU都有自身對應的高速緩沖區(多級寄存器),每個線程被執行的時候,會先把運行時需要的數據復制到告訴緩沖區一份,此高速緩存區只與在該CPU運行的線程有關,然后在當前線程需要CPU執行N多指令的時候,就不用再去內存中拿數據,直接從本地的緩沖區,進而提高CPU的執行任務速度,等待執行完畢后再把結果寫入到主內存中,但是什么時候執行結果會被刷新至主內存中是不太確定的(但是肯定在執行下一指令之前,哈哈);在遇到線程放棄執行權限或者sleep一段時間后等再次被處理器運行的時候,會重新把需要的數據載入高速緩沖區中。
上面這個結構,對于單CPU來說沒有任何問題,但是近代計算機一般都是多個CPU,這樣一來,每個CPU的高速緩沖區如果同時緩存了共享變量的話,那么就有可能出現數據狀態不一致的情況,那么這個情況怎么解決呢?兩個解決方案:
緩存一致性協議也稱MESI協議
- 獨占(exclusive):僅當前處理器擁有該緩存行,并且沒有修改過;
- 共享(share):有多個處理器擁有該緩存行,每個處理器都沒有修改過緩存;
- 修改(modified):僅當前處理器擁有該緩存行,并且緩存行被修改過了;
- 失效(invalid):緩存行被其他處理器修改過;
它們之間的關系如下:
- 一個處于M狀態的緩存行,必須時刻監聽所有試圖讀取該緩存行對應的主存地址的操作,如果監聽到,則必須在此操作執行前把其緩存行中的數據寫回CPU;
- 一個處于S狀態的緩存行,必須時刻監聽使該緩存行無效或者獨享該緩存行的請求,如果監聽到,則必須把其緩存行狀態設置為I;
- 一個處于E狀態的緩存行,必須時刻監聽其他試圖讀取該緩存行對應的主存地址的操作,如果監聽到,則必須把其緩存行狀態設置為S
- 當CPU需要讀取數據時,如果其緩存行的狀態是I的,則需要從內存中讀取,并把自己狀態變成S,如果不是I,則可以直接讀取緩存中的值,但在此之前,必須要等待其他CPU的監聽結果,如其他CPU也有該數據的緩存且狀態是M,則需要等待其把緩存更新到內存之后,再讀取;
- 當CPU需要寫數據時,只有在其緩存行是M或者E的時候才能執行,否則需要發出特殊的RFO指令(Read Or Ownership,這是一種總線事務),通知其他CPU置緩存無效(I),這種情況下會性能開銷是相對較大的。在寫入完成后,修改其緩存狀態為M。
Volatile的指令重排
處理器的內存屏障分為兩種:Load Barrier 和 Store Barrier即讀屏障和寫屏障。
- 阻止屏障兩側的指令重排序;
- 強制把寫緩沖區/高速緩存中的臟數據等寫回主內存,讓緩存中相應的數據失效。
JVM的內存屏障其實也是對計算機內存屏障的封裝,其兼容了不容平臺的差異,通過調用硬件的內存屏障指令來實現禁止指令重排。
在每個volatile寫操作的前面插入一個StoreStore屏障。
在每個volatile寫操作的后面插入一個StoreLoad屏障。
在每個volatile讀操作的后面插入一個LoadLoad屏障。
在每個volatile讀操作的后面插入一個LoadStore屏障。
Volatile的內存可見性
JMM內存模型與上面說的類似,對于共享變量每個線程都會對其生成變量副本,在后續的讀寫操作中都是操作其副本,等待對變量操作完畢后,再把變量副本寫入到主內存中(不是實時寫入主內存),如果遇到多個線程同時讀寫共享變量的時候,由于他們都是操作的副本,所以各個線程之間是互不知曉的,那么怎么讓其中一個線程修改變量的時候,另外一個線程立馬就知道呢?通過對volatile修飾的共享變量相關代碼進行編譯生成的匯編指令發現,volatile寫操作對應的指令是一個lock前綴指令,而lock前綴指令會在多核CPU同時運行的情況下引發兩件事:
- CPU高速緩沖區中對應該變量的緩存行數據立馬回寫到主內存。在老版本的多核處理器中,lock前綴指令會在執行lock指令的時候聲言LOCK#信號,該信號確保在執行此指令期間,此處理器可以獨享任何內存,因為它會鎖住總線,導致其他CPU不能訪問總線進而不能訪問系統內存;由于鎖總線實在是性能消耗太大,所以在近代處理器中會鎖定此共享變量的內存緩沖區(由緩存行組成),而其他CPU則無法訪問對應緩沖區中鎖定的那部分數據,進而實現鎖定的那部分數據同一時間只有一個CPU可以操作;使用緩存一致性協議(MESI)來保證原子性,緩存一致性機制不允許同時修改被多個CPU緩存了的內存區域。
- 觸發其他CPU緩沖區中對應的緩沖行失效。使用嗅探技術,探測其他CPU的緩存以及主內存,如果探測到共享變量有改變或者預計有寫入操作,處理器將會把對應的緩存行置為無效。
- 據查資料,任何的lock前綴指令都有內存屏障的作用。
JMM內存模型驗證
private static boolean exit = false;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(!exit) {
//try {
//System.out.println("continue...");
//Thread.currentThread().sleep(1000);
//} catch (InterruptedException e) {}
}
System.out.println("over...");
}).start();
Thread.currentThread().sleep(2000);
exit = true;
}
執行上面這段程序,你會發現程序會一直運行,但是將exit變量聲明為volatile的時候,2s就停止了。
但是你把try代碼塊注釋打開的話,那么你會發現雖然exit變量不是volatile的,但是程序也會在2停止,為什么呢?猜測原因有二:
有不對的地方,歡迎大家指正!
歡迎大家點贊評論關注收藏轉發,關注微信公眾號:芝麻技術棧
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的cpu缓冲区大小怎么设置_JAVA高薪面试必备知识点Volatile底层原理探究CPU在作怪的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 取两个数较小值c语言_编程代码:用C语言
- 下一篇: python日期函数_python 时间