阿里一道Java併發面試題 (詳細分析篇)
題目
我個人一直認為:網路、併發相關的知識,相對其他一些程式設計知識點更難一些,主要是不好除錯並且涉及內容太多 !
所以今天就取一篇併發相關的內容分享下,我相信大家認真看完會有收穫的。
大家可以先看看這個問題,看看這個是否有問題呢? 那裡有問題呢?
如果你在這個問題上面停留超過5s的話,那麼表示你對這塊某些知識還有點模糊,需要再鞏固下,下面我們一起來分析下!
結論
多執行緒併發的同時進行set、get操作, A執行緒呼叫set方法,B執行緒並一定能對這個改變可見!!!
分析
這個類非常簡單,裡面有一個屬性,有2個方法:get、set方法,一個用來設定屬性值,一個用來獲取屬性值,在設定屬性方法上面加了synchronized。
隱式資訊:多執行緒併發的同時進行set、get操作, A執行緒呼叫set方法,B執行緒可以裡面感知到嗎???
說到這裡, 問題就變成了synchronized在剛剛說的上下文下面能否保證可見性!!!
關鍵詞synchronized的用法
指定加鎖物件:對給定物件加鎖,進入同步程式碼前需要獲得給定物件的鎖。
直接作用於例項方法:相當於對當前例項加鎖,進入同步程式碼前要獲得當前例項的鎖。
直接作用於靜態方法:相當於對當前類加鎖,進入同步程式碼前要獲得當前類的鎖。
synchronized它的工作就是對需要同步的程式碼加鎖,使得每一次只有一個執行緒可以進入同步塊(其實是一種悲觀策略)從而保證執行緒之間得安全性。
從這裡我們可以知道,我們需要分析的屬於第二類情況,也就是說多個執行緒如果同時進行set方法的時候,由於存在鎖,所以會一個一個進行set操作,並且是執行緒安全的,但是get方法並沒有加鎖,表示假如A執行緒在進行set的同時B執行緒可以進行get操作。並且可以多個執行緒同時進行get操作,但是同一時間最多隻能有一個set操作。
Java 記憶體模型 happens-before原則
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執行緒並一定能對這個改變可見!
volatile
volatile可見性
前面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方法並不可見!!!
新增volatile 關鍵詞觀察效果
其例項子中synchronized關鍵字可以去掉,僅僅用volatile即可。
效果:
程式碼很快正常結束了!
加架構群:705127209 領取資料,裡面會分享一些資深架構師錄製的影片錄影:有Spring,MyBatis,Netty原始碼分析,高併發、高效能、分散式、微服務架構的原理,JVM效能最佳化這些成為架構師必備的資料
結論:
多執行緒併發的同時進行set、get操作, A執行緒呼叫set方法,B執行緒並一定能對這個改變可見!!! ,上面的程式碼中,如果對get方法也加synchronized也是可見的,還是happens-before的 監視器鎖規則:對一個監視器的解鎖,happens-before 於隨後對這個監視器的加鎖。 ,只是volatile比synchronized更輕量級,所以本例直接用volatile。但是對於符合原子操作i++這裡還是不行的還是需要synchronized。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912582/viewspace-2643958/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java併發面試題Java面試題
- 【Java併發專題】27篇文章詳細總結Java併發基礎知識Java
- Java 併發面試題解Java面試題
- 一道面試題的分析面試題
- Java併發面試題精選Java面試題
- 超詳細的Java面試題總結(三)之Java集合篇常見問題Java面試題
- java面試題總結-詳細分類Java面試題
- Java面試之多執行緒&併發篇Java面試執行緒
- 【搞定 Java 併發面試】面試最常問的 Java 併發基礎常見面試題總結!Java面試題
- Java面試之多執行緒&併發篇(2)Java面試執行緒
- Java面試之多執行緒&併發篇(5)Java面試執行緒
- Java面試之多執行緒&併發篇(3)Java面試執行緒
- Java面試之多執行緒&併發篇(4)Java面試執行緒
- Java面試之多執行緒&併發篇(6)Java面試執行緒
- Java面試之多執行緒&併發篇(8)Java面試執行緒
- Java面試之多執行緒&併發篇(9)Java面試執行緒
- Java面試之多執行緒&併發篇(7)Java面試執行緒
- Java 面試(二)| 詳細的MySql面試部分Java面試MySql
- Java併發--Java執行緒面試題 Top 50Java執行緒面試題
- 一道阿里java多執行緒面試題的go版本實現阿里Java執行緒面試題Go
- 【Java併發程式設計】面試常考的ThreadLocal,超詳細原始碼學習Java程式設計面試thread原始碼
- 詳解Java 容器(第⑤篇)——容器原始碼分析 - 併發容器Java原始碼
- Kafka詳細教程加面試題Kafka面試題
- 一道面試題引發的思考面試題
- 一道面試題引發的“血案”面試題
- 面試系列<3>——java併發面試Java
- Java面試題-javaweb篇六Java面試題Web
- Java面試題-javaweb篇七Java面試題Web
- Java面試題-框架篇八Java面試題框架
- Java面試題-框架篇九Java面試題框架
- Java面試題:之ZooKeeper篇Java面試題
- java面試題核心篇(一)Java面試題
- java面試題核心篇(二)Java面試題
- java面試題核心篇(三)Java面試題
- RabbitMQ/高併發面試題MQ面試題
- 最新阿里Java面試題,這些面試題你會嗎?阿里Java面試題
- 2019 阿里java面試總結 (含面試題解析)阿里Java面試題
- Java併發程式設計40道面試題及答案——面試穩了Java程式設計面試題