併發程式設計(三)執行緒安全性

2021-10-21 14:37:21 字數 2312 閱讀 2173

常見問題:

共享資源是否有多個執行緒同時訪問

希望結果跟預期的一致

作用:保證共享資源的可見性

如何保證可見性(hsdis工具)?

通過反編譯可以看到多了乙個彙編lock指令,相當於下面說的記憶體屏障的功能

硬體層面

cpu的快取記憶體:分為l1(指令快取)、l2(資料快取)、l3 -->效能逐步下降

為了最大化利用cpu資源:優化了如下模組

cpu增加快取記憶體

引入執行緒、程序

指令優化:重排序

硬體層面解決可見性

匯流排鎖:處理器之間的操作互斥->效能差

cpu獲取匯流排鎖,匯流排發出lock訊號,其他處理器不能操作主記憶體

快取鎖:降低鎖力度

如何實現快取鎖?通過mesi協議

cpu快取一致性協議(mesi):快取的4種狀態

m (modify) 共享資料快取在當前cpu中,並且是修改狀態,即快取資料和主記憶體不一致

e (exclusive) 資料快取在當前cpu中,獨佔並且沒有被修改

s (shared) 資料被多個cpu快取,並且與主存一致

i (invalid) 表示快取已經失效

讀請求:m、e、s狀態的快取可以被讀取,i狀態只能從主存中讀取

寫請求:m、e狀態可以寫,s狀態需要使其他cpu快取失效

**引入快取鎖後仍存在的問題:**處於s狀態的cpu進行寫請求的時候需要跟其他cpu通訊,這就會存在阻塞問題

因此引入storebuffer緩衝區(相當於非同步處理),在cpu當中,主記憶體讀取先讀buffer,但是仍有重排序等問題

在沒有屏障指令的情況下,兩個cpu同時執行,cpu0對於value=10先寫入storebuffer在通知cpu1,繼續執行isfinish,因為e狀態可以直接修改修改。cpu1讀取到isfinish為true,但此時的value還未修改為10,因此assert判斷為false

cpu層面提供了指令->記憶體屏障

記憶體屏障:用來解決可見性問題

cpu層面提供了3種屏障

寫屏障:寫之前的快取同步到主存

讀屏障:基於失效佇列

全屏障

jmm的記憶體模型層面

導致可見性的根本原因:快取記憶體和重排序

jmm的核心是解決了有序性和可見性

重排序的型別:

編譯重排序

cpu層面的重排序:指令重排序、記憶體重排序

不會進行重排序的場景:

資料依賴規則

as-if-serial原則:不管如何重排序,結果不能變

jvm語言級別的記憶體屏障:

loadload、storestore、loadstore、storeload、

程式順序規則:單執行緒對於結果來說是不變的

volatile變數規則:volatile修飾的變數,寫操作對於後續讀操作可見

傳遞性規則:1 h-b 2,2 h-b 3,則1 h-b 3

start規則:執行緒a中啟動b執行緒,則啟動前的資料對於執行緒b可見

join規則:執行緒b join 執行緒a,則執行緒a的共享變數操作對於b可見

監視器鎖規則:對於鎖(synchronized)的解鎖對於這個鎖的加鎖可見,即多執行緒之間synchronized**塊之間是順序可見

mesi協議實現可見性和有序性:

無法完全實現。由於mesi store buffer當中是非同步的,可能會造成有序性問題,因此通過記憶體屏障來解決

併發程式設計的安全性(2)

安全 安全的首先是正確的且是我們預期的,正確性 某個類的行為與其規範完全一致。在良好的規範中通常會定義各種不變性條件來約束物件的狀態,以及定義各種後驗條件來描述物件操作的結果。我們根據這些規範在單執行緒中執行獲取正確的預期結果,代表這個程式的正確性,即所見即所知。而當多個執行緒同時訪問某個類,這個類...

執行緒安全性

定義 當多個執行緒訪問某個類時,不管執行環境採用何種呼叫方式或者這些執行緒如何交替執行,並且在主調 中不需要任何額外的同步或者協同,這個類都能表現出正確的行為,那麼就稱這個類是執行緒安全的。主要表現三個方面 atomic cas unsafe.compareandswapint atomiclong...

執行緒安全性

執行緒安全性 當多個執行緒訪問某個類時,這個類始終都能表現出正確的行為,那麼稱這個類是執行緒安全的。執行緒不安全產生的問題 競態條件 由於不恰當的執行時序而出現不正確的結果。大多數競態條件的本質是基於一種可能失效的觀察結果來做出判斷或者執行某個計算。常見先檢查後執行,延遲初始化 單例模式 讀取 修改...