從硬體的級別來考慮一下可見性的問題(面試)

2021-10-07 19:02:43 字數 1644 閱讀 8345

每個處理器都有自己的暫存器(register),所以多個處理器各自執行乙個執行緒的時候,可能導致某個變數給放到暫存器裡去,接著就會導致各個執行緒沒法看到其他處理器暫存器裡的變數的值修改了。

可見性的第乙個問題,首先,就有可能在暫存器的級別,導致變數副本的更新,無法讓其他處理器看到。

然後乙個是處理器執行的執行緒對變數的寫操作都是針對寫緩衝來的(store buffer)並不是直接更新主記憶體,所以很可能導致乙個執行緒更新了變數,但是僅僅是在寫緩衝區裡罷了,沒有更新到主記憶體裡去。

這個時候,其他處理器的執行緒是沒法讀到他的寫緩衝區的變數值的,所以此時就是會有可見性的問題,這是第二個可見性發生的場景。

然後即使這個時候乙個處理器的執行緒更新了寫緩衝區之後,將更新同步到了自己的快取記憶體裡(cache,或者是主記憶體),然後還把這個更新通知給了其他的處理器,但是其他處理器可能就是把這個更新放到無效佇列裡去,沒有更新他的快取記憶體。此時其他處理器的執行緒從快取記憶體裡讀資料的時候,讀到的還是過時的舊值。

可見性發生的問題

如果要實現可見性的話,其中乙個方法就是通過mesi協議,這個mesi協議實際上有很多種不同的實現,因為他不過就是乙個協議罷了,具體的實現機制要靠具體底層的系統如何實現。根據具體底層硬體的不同,mesi協議的實現是有區別的。

比如說mesi協議有一種實現,就是乙個處理器將另外乙個處理器的快取記憶體中的更新後的資料拿到自己的快取記憶體中來更新一下,這樣大家的快取不就實現同步了,然後各個處理器的執行緒看到的資料就一樣了。

為了實現mesi協議,有兩個配套的專業機制要說一下:flush處理器快取、refresh處理器快取。

flush處理器快取,他的意思就是把自己更新的值重新整理到快取記憶體裡去(或者是主記憶體),因為必須要刷到快取記憶體(或者是主記憶體)裡,才有可能在後續通過一些特殊的機制讓其他的處理器從自己的快取記憶體(或者是主記憶體)裡讀取到更新的值。除了flush以外,他還會傳送乙個訊息到匯流排(bus),通知其他處理器,某個變數的值被他給修改了。

refresh處理器快取,他的意思就是說,處理器中的執行緒在讀取乙個變數的值的時候,如果發現其他處理器的執行緒更新了變數的值,必須從其他處理器的快取記憶體(或者是主記憶體)裡,讀取這個最新的值,更新到自己的快取記憶體中。所以說,為了保證可見性,在底層是通過mesi協議、flush處理器快取和refresh處理器快取,這一整套機制來保障的。

flush和refresh,這兩個操作,flush是強制重新整理資料到快取記憶體(主記憶體),不要僅僅停留在寫緩衝器裡面;refresh,是從匯流排嗅探發現某個變數被修改,必須強制從其他處理器的快取記憶體(或者主記憶體)載入變數的最新值到自己的快取記憶體裡去。

記憶體屏障的使用,在底層硬體級別的原理,其實就是在執行flush和refresh,mesi協議是如何與記憶體屏障搭配使用的(flush、refresh)

volatile boolean isrunning = true;

isrunning = false; => 寫volatile變數,就會通過執行乙個記憶體屏障,在底層會觸發flush處理器快取的操作;while(isrunning) {},讀volatile變數,也會通過執行乙個記憶體屏障,在底層觸發refresh操作。

volatile關鍵字的作用:對乙個變數加了volatile修飾之後,對這個變數的寫操作,會執行flush處理器快取,把資料刷到快取記憶體(或者是主記憶體)中,然後對這個變數的讀操作,會執行refresh處理器快取,從其他處理器的快取記憶體(或者是主記憶體)中,讀取最新的值。

等間距布局 從0開始說一下masonry的使用

以下將從幾個方面說一下如何使用masonry 怎樣新增約束才能滿足乙個view,及masonry的基本使用 如何使用masonry等間隙排布幾個view 更新約束動畫 scrolview如何布局 tableviewcell高度動態變化 先直接上圖,最終要實現這樣乙個布局 這裡一共三部分,最上面黃色的...

記錄一下從南京到杭州的4月份的面試經歷

一 離職背景 在南京一家傳統公司呆了快兩年了吧,基本都是個增刪改查的需求,然後負責帶帶新人,本來就有點厭倦了。後來老版本的專案要更新換代,整測了所有模組,於是從16年到19的bug一次性堆積出來,lz前前後猴大概改了600多個bug吧,直接把我改沒了,直接提了辭職報告,真的想讓乙個員工離職,讓他一次...

請詳細描述一下執行緒從建立到死亡的幾種狀態都有哪些?

1.新建 new 新建立了乙個執行緒物件。2.可執行 runnable 執行緒物件建立後,其他執行緒 比如 main 執行緒 呼叫了該物件 的 start 方法。該狀態的執行緒位於可執行執行緒池中,等待被執行緒排程選中,獲 取 cpu 的使用權 3.執行 running 可執行狀態 runnable...