導航:首頁 > 源碼編譯 > 編譯器和處理器重排規則

編譯器和處理器重排規則

發布時間:2024-09-07 15:11:18

1. 線程鎖的原理是什麼

線程鎖的原理:當對象獲取鎖時,它首先使自己的高速緩存無效,這樣就可以保證直接從主內存中裝入變數。

同樣,在對象釋放鎖之前,它會刷新其高速緩存,強制使已做的任何更改都出現在主內存中。 這樣,會保證在同一個鎖上同步的兩個線程看到在 synchronized 塊內修改的變數的相同值。

一般來說,線程以某種不必讓其他線程立即可以看到的方式(不管這些線程在寄存器中、在處理器特定的緩存中,還是通過指令重排或者其他編譯器優化),不受緩存變數值的約束。

(1)編譯器和處理器重排規則擴展閱讀:

線程鎖在run()函數中使用QMutex實現同步,當多個線程訪問共享變數時,使用lock/trylock和unlock將共享變數包裹,以保證同步訪問共享變數。

如果不加鎖將會在2秒後同時修改num變數,將會導致線程不按照我們的想法執行,當前線程鎖定後,其他線程如果遇到共享變數將會等待解鎖;

使用QMutex上鎖解鎖時,當代碼提前退出有可能並未執行unlock(),若其他線程採用lock上鎖會一直被阻塞,導致內存溢出。

2. java中volatile修飾的變數有什麼特徵

volatile是一個類型修飾符,它是被設計用來修飾被不同線程訪問和修改的變數,可以被非同步的線程所修改。
final必須對它賦予初值並且不能修改它。
對比就知道兩個修飾符是沖突的,放一起是要干什麼呢?

3. Java內存模型詳解

理解Java內存模型是深入學習Java並發不可或缺的部分。Java內存模型即JavaMemoryModel,簡稱為JMM,定義了多線程之間共享變數的可見性以及如何在需要的時候對共享變數進行同步。

JMM規定Java線程間的通信採用共享內存的方式。在Java中,所有成員變數、靜態變數和數組元素都存儲在堆內存中,堆內存在線程之間共享,所以它們通常也稱為共享變數。

JMM定義了線程和主內存之間的抽象關系:線程之間的共享變數存儲在主內存(MainMemory)中,每個線程都有一個私有的本地內存(LocalMemory,或者也可以稱為工作內存WorkMemory),本地內存中存儲了該線程以讀/寫共享變數的副本。本地內存是JMM的一個抽象概念,並不真實存在。它涵蓋了緩存、寫緩沖區、寄存器以及其他的硬體和編譯器優化。

JMM抽象

JMM的抽象示意圖如下所示:

多個線程同時對同一個共享變數進行讀寫的時候會產生線程安全問題。那為什麼CPU不直接操作內存,而要在CPU和內存間加上各種緩存和寄存器等緩沖區呢?因為CPU的運算速度要比內存的讀寫速度快得多,如果CPU直接操作內存的話勢必會花費很長時間等待數據到來,所以緩存的出現主要是為了解決CPU運算速度與內存讀寫速度不匹配的矛盾。

內存間交互協議

JMM規定了主內存和工作內存間具體的交互協議,即一個變數如何從主內存拷貝到工作內存、如何從工作內存同步到主內存之間的實現細節,這主要包含了下面8個步驟:

lock(鎖定):作用於主內存的變數,把一個變數標識為一條線程獨占狀態。

unlock(解鎖):作用於主內存變數,把一個處於鎖定狀態的變數釋放出來,釋放後的變數才可以被其他線程鎖定。

read(讀取):作用於主內存變數,把一個變數值從主內存傳輸到線程的工作內存中,以便隨後的load動作使用

load(載入):作用於工作內存的變數,它把read操作從主內存中得到的變數值放入工作內存的變數副本中。

use(使用):作用於工作內存的變數,把工作內存中的一個變數值傳遞給執行引擎,每當虛擬機遇到一個需要使用變數的值的位元組碼指令時將會執行這個操作。

assign(賦值):作用於工作內存的變數,它把一個從執行引擎接收到的值賦值給工作內存的變數,每當虛擬機遇到一個給變數賦值的位元組碼指令時執行這個操作。

store(存儲):作用於工作內存的變數,把工作內存中的一個變數的值傳送到主內存中,以便隨後的write的操作。

write(寫入):作用於主內存的變數,它把store操作從工作內存中一個變數的值傳送到主內存的變數中。lock,unlock需要代碼中用鎖實現。

這8個步驟必須符合下述規則:

不允許read和load,store和write操作之一單獨出現。

不允許一個線程丟棄它最近的assign操作。即變數在工作內存中改變了賬號必須把變化同步回主內存。

一個新的變數只允許在主內存中誕生,不允許工作內存直接使用未初始化的變數。

一個變數同一時刻只允許一條線程進行lock操作,但同一線程可以lock多次,lock多次之後必須執行同樣次數的unlock操作。

如果對一個變數進行lock操作,那麼將會清空工作內存中此變數的值。

不允許對未lock的變數進行unlock操作,也不允許unlock一個被其它線程lock的變數。

如果一個變數執行unlock操作,必須先把此變數同步回主內存中。

指令重排

在執行程序時,為了提高性能,編譯器和處理器常常會對指令做重排序。從Java源代碼到最終實際執行的指令序列,會分別經歷下面3種重排序:

編譯器優化的重排序。編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序。

指令級並行的重排序。現代處理器採用了指令級並行技術(Instruction-LevelParallelism,ILP)來將多條指令重疊執行。如果不存在數據依賴性,處理器可以改變語句對應機器指令的執行順序。

內存系統的重排序。由於處理器使用緩存和讀/寫緩沖區,這使得載入和存儲操作看上去可能是在亂序執行。

如果兩個操作訪問同一個變數,其中一個為寫操作,此時這兩個操作之間存在數據依賴性。編譯器和處理器不會改變存在數據依賴性關系的兩個操作的執行順序,即不會重排序。不管怎麼重排序,單線程下的執行結果不能被改變,編譯器、runtime和處理器都必須遵守as-if-serial語義。

內存屏障

通過插入內存屏障(MemoryBarrier)可以阻止特定類型的指令重排。JMM將內存屏障劃分為四種:

屏障類型示例描述LoadLoadBarriersLoad1-LoadLoad-Load2Load1數據裝載過程要先於Load2及所有後續的數據裝載過程StoreStoreBarriersStore1-StoreStore-Store2Store1刷新數據到內存的過程要先於Strore2及後續所有刷新數據到內存的過程LoadStoreBarriersLoad1-LoadStore-Store2Load1數據裝載要先於Strore2及後續所有刷新數據到內存的過程StoreLoadBarriersStore1-StoreLoad-Load2Store1刷新數據到內存的過程要先於Load2及所有後續的數據裝載過程

Java中volatile關鍵字的實現就是通過內存屏障來完成的。

happens-before

從jdk5開始,java使用新的JSR-133內存模型,基於happens-before的概念來闡述操作之間的內存可見性。

在JMM中,如果一個操作的執行結果需要對另一個操作可見,那麼這兩個操作之間必須要存在happens-before關系,這個的兩個操作既可以在同一個線程,也可以在不同的兩個線程中。

程序員密切相關的happens-before規則如下:

程序順序規則:一個線程中的每個操作,happens-before於該線程中任意的後續操作。

監視器鎖規則:對一個鎖的解鎖操作,happens-before於隨後對這個鎖的加鎖操作。

volatile域規則:對一個volatile域的寫操作,happens-before於任意線程後續對這個volatile域的讀。

傳遞性規則:如果Ahappens-beforeB,且Bhappens-beforeC,那麼Ahappens-beforeC。

注意:兩個操作之間具有happens-before關系,並不意味前一個操作必須要在後一個操作之前執行!僅僅要求前一個操作的執行結果,對於後一個操作是可見的,且前一個操作按順序排在後一個操作之前。

閱讀全文

與編譯器和處理器重排規則相關的資料

熱點內容
安卓路由表怎麼看 瀏覽:74
10個小孩分糖果python 瀏覽:823
安卓app小紅點怎麼取消 瀏覽:286
馬哥linux面授 瀏覽:768
2345程序員 瀏覽:371
怎麼開啟網速顯示安卓 瀏覽:204
索尼950app怎麼樣 瀏覽:391
計算機畢設論文源碼 瀏覽:148
小米手機怎麼下載易媒體App 瀏覽:191
還原系統設計可以怎樣清理文件夾 瀏覽:670
北京時間校準伺服器雲伺服器 瀏覽:844
pythonexcel數據計算 瀏覽:49
內醫附院黨建APP怎麼下載 瀏覽:356
榮耀v20刷加密門禁卡 瀏覽:329
app緩沖視頻怎麼保存在手機 瀏覽:433
廣度優先演算法c語言 瀏覽:680
提高程序員競爭力 瀏覽:108
nfc可以開加密門卡嗎 瀏覽:675
如何知道網頁使用的伺服器是什麼 瀏覽:224
單片機接反電源會燒嗎 瀏覽:287