您好,登錄后才能下訂單哦!
這篇文章主要講解了“Java并發面試題實例分析”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java并發面試題實例分析”吧!
多線程并發的同時進行set、get操作, A線程調用set方法,B線程并一定能對這個改變可見!!!
這個類非常簡單,里面有一個屬性,有2個方法:get、set方法,一個用來設置屬性值,一個用來獲取屬性值,在設置屬性方法上面加了synchronized。
隱式信息:多線程并發的同時進行set、get操作, A線程調用set方法,B線程可以里面感知到嗎???
說到這里, 問題就變成了synchronized在剛剛說的上下文下面能否保證可見性!!!
指定加鎖對象:對給定對象加鎖,進入同步代碼前需要獲得給定對象的鎖。
直接作用于實例方法:相當于對當前實例加鎖,進入同步代碼前要獲得當前實例的鎖。
直接作用于靜態方法:相當于對當前類加鎖,進入同步代碼前要獲得當前類的鎖。
synchronized它的工作就是對需要同步的代碼加鎖,使得每一次只有一個線程可以進入同步塊(其實是一種悲觀策略)從而保證線程之間得安全性。
從這里我們可以知道,我們需要分析的屬于第二類情況,也就是說多個線程如果同時進行set方法的時候,由于存在鎖,所以會一個一個進行set操作,并且是線程安全的,但是get方法并沒有加鎖,表示假如A線程在進行set的同時B線程可以進行get操作。并且可以多個線程同時進行get操作,但是同一時間最多只能有一個set操作。
JSR-133 內存模型使用 happens-before 的概念來闡述操作之間的內存可見性。在 JMM 中,如果 一個操作執行的結果需要對另一個操作可見 ,那么這兩個操作之間必須要存在 happens-before 關系。這里提到的兩個操作既可以是在一個線程之內,也可以是在不同線程之間。
與程序員密切相關的 happens-before 規則如下:
程序順序規則:一個線程中的每個操作,happens-before 于該線程中的任意后續操作。
監視器鎖規則:對一個監視器的解鎖,happens-before 于隨后對這個監視器的加鎖。
volatile 變量規則:對一個 volatile 域的寫,happens-before 于任意后續對這個 volatile 域的讀。
傳遞性:如果 A happens-before B,且 B happens-before C,那么 A happens-before C。
注意,兩個操作之間具有 happens-before 關系,并不意味著前一個操作必須要在后一個操作之前執行!happens-before 僅僅要求前一個操作(執行的結果)對后一個操作可見,且前一個操作按順序排在第二個操作之前(the first is visible to and ordered before the second)。
其中有 監視器鎖規則:對一個監視器的解鎖,happens-before 于隨后對這個監視器的加鎖。這一條,僅僅只是針對synchronized的set方法,而對于get并沒有這方面的說明。
其實在這種上下文下面一個synchronized的set方法,一個普通的get方法,a線程調用set方法,b線程并一定能對這個改變可見!
前面happens-before原則就提到: volatile 變量規則:對一個 volatile 域的寫,happens-before 于任意后續對這個 volatile 域的讀。 volatile從而保證了多線程下的可見性!!!
volatile 禁止內存重排序
下面是 JMM 針對編譯器制定的 volatile 重排序規則表:
為了實現 volatile 的內存語義,編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。
下面是基于保守策略的 JMM 內存屏障插入策略:
在每個 volatile 寫操作的前面插入一個 StoreStore 屏障。
在每個 volatile 寫操作的后面插入一個 StoreLoad 屏障。
在每個 volatile 讀操作的后面插入一個 LoadLoad 屏障。
在每個 volatile 讀操作的后面插入一個 LoadStore 屏障。
下面是保守策略下,volatile 寫操作 插入內存屏障后生成的指令序列示意圖:
下面是在保守策略下,volatile 讀操作 插入內存屏障后生成的指令序列示意圖:
上述 volatile 寫操作和 volatile 讀操作的內存屏障插入策略非常保守。在實際執行時,只要不改變 volatile 寫-讀的內存語義,編譯器可以根據具體情況省略不必要的屏障。
通過上面的分析,其實這個題目涉及到的內容都提到了,并且進行了解答。
雖然你知道的原因,但是想模擬并不是一件容易的事情!,下面我們來模擬看看效果:
publicclassThreadSafeCache{intresult;publicintgetResult(){returnresult; }publicsynchronizedvoidsetResult(intresult){this.result = result; }publicstaticvoidmain(String[] args){ ThreadSafeCache threadSafeCache =newThreadSafeCache();for(inti =0; i <8; i++) {newThread(() -> {intx =0;while(threadSafeCache.getResult() <100) { x++; } System.out.println(x); }).start(); }try{ Thread.sleep(1000); }catch(InterruptedException e) { e.printStackTrace(); } threadSafeCache.setResult(200); }}
效果:
程序會一直卡在這邊不動,表示set修改的200,get方法并不可見!!!
其實例子中synchronized關鍵字可以去掉,僅僅用volatile即可。
效果:
代碼很快正常結束了!
感謝各位的閱讀,以上就是“Java并發面試題實例分析”的內容了,經過本文的學習后,相信大家對Java并發面試題實例分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。