Java併發編程常識

這是why的第 85 篇原創文章

寫中間件經常要做兩件事:

  • 1.延遲加載,在內存緩存已加載項。
  • 2.統計調用次數,攔截併發量。

就這麼個小功能,團隊裏的人十有八九寫錯。


上面這句話不是我說的,是梁飛在他的博客裏面說的。

梁飛是誰?

據網上的公開資料,梁飛,花名虛極。

2009 年加入阿里巴巴,負責中間件的開發,Dubbo 開源分佈式服務框架作者,HTTL 開源模板引擎作者。

2012 年加入天貓,負責手機天貓 APP 的技術團隊,見證了天貓雙 11 無線化全過程。

熱衷參與開源社區建設,傳播服務化、SOA、框架設計、移動應用等架構設計理念。

下面這段是我在他的個人博客裏面找到的:

https://www.iteye.com/blog/javatar-287026

上週五去杭州面試,早上飛過去,晚上飛回來,有點匆忙,飛機晚點,到那邊的時候都快下午一點了,面試了一個小時左右,主要是筆試沒做了,省了不少時間。

面試完後,跟着幾個熟人轉了一圈,學習交流氣氛都很好。

週二收到了Offer,看來HR效率很高,贊一個,定的是1月8號入職。

這兩天忙於提交辭職申請,就要離開奮鬥了近三年的公司,還是有點不捨,公司給了我很多發展機會,領導們對我也特別好,真的很感謝他們。

只是現在新的公司發展機遇更好,我不想放棄,希望在新的崗位上能做得更好,呵呵,流水帳記到這裏。

這篇博客,發佈於 2008 年 12 月 24 日。

他說的上週五,也就是 2008 年 12 月 19 日。一天之內,從深圳飛到杭州,面試一小時,又從杭州飛回深圳,4 天之後,收到了阿里的 offer。

彼時,梁飛也僅從湖南科技職院軟件學院的大門走出來不到 3 年的時間。

文章裏面說的“1 月 8 號入職”,也就是 2009 年的 1 月 8 日。

從此,在阿里開啓了長達 12 年的升級打怪之路,從 P5 一路殺到了 P9。

衆所周知,阿里能到 P9 ,已經不是能力的事兒了,這屬於祖上積德。

而梁飛在 2016 年的時候就已經是 P9 了。

上面說的這麼多標籤,而他最爲人熟知的標籤應該是:Dubbo 創始人。

在梁飛加入阿里的 1015 天之後,也就是 2011 年 10 月 20 日 23 點 06 分,Dubbo 在開源社區發出了第一聲啼鳴:

9 個模塊,共計 669 個文件,就是日後這個一路坎坎坷坷、幾近夭折、友商續命,最終成爲 Apache 頂級開源項目的雛形。

2011 年 10 月 20 日那天晚上,對於梁飛來說註定是一個回憶起來,難以忘懷的夜晚。

他一直進行了13 次提交:

終於在第二天的早上 5 點 25 分給 Dubbo 打上了第一個 tag:2.0.7。

期間還抽空通知官媒發了個微博:

而他自己,第二天的中午,也在自己的博客上公佈了這件事情:

爲什麼 Dubbo 會選在這一天進行開源呢?

我想應該是爲了趕上兩天之後的 Qcon 全球軟件開發者大會:

那一天,纔是 Dubbo 真正意義上,站在大衆視野裏,接受讚揚與嘲諷的開始。

我千辛萬苦,找到了近 10 年前,那次大會的分享 PPT,開篇第一頁:

十年過去了,白雲蒼狗,Dubbo 從阿里走了出來,已經真正的飛入尋常百姓家了。

十年間,PPT 上的數據,從總量上來看,就像雙十一的銷售額一樣,不知道翻了多少個翻。

但是轉念一想,這是 10 年前的阿里,每天 10 億次的調用,這是多少技術人一生也接觸不到的流量啊。

同時,我在 PPT 裏面也發現很多熟悉的圖片,比如這張:

左邊是現在的 Dubbo 官網,右邊是 10 年前 Qcon 大會上的 PPT。

10 年過去了,當這兩張圖片同框的時候,不知道右邊的“老圖”,有沒有一種老父親般的欣慰。

如果說上面這個圖你的感覺還不是特別的強烈,那麼再看看這個:

左邊是官網,右邊是 PPT。

10 年過去了,整體設計沒有發生一絲變化。

我不知道你看到這裏的時候想到的是什麼,於我而言,真牛逼啊。

一個 10 年前的整體設計,竟然在日新月異、風起雲湧的互聯網行業一絲不變的屹立了下來。

這是一羣工程師智慧的結晶。

Dubbo 第一次面世的 PPT 有 55 頁,裏面有很多設計從十年後的今天看去也不過時。

我就不一一展示了,有興趣的朋友,在文末可以看到獲取方式。

最後,獻上一張最早的 Dubbo 團隊的合照:

Java併發編程常識

還記得本文開篇的那句話嗎?

https://www.iteye.com/blog/javatar-1963774

這裏的 PPT 鏈接失效了,我歷盡千辛萬苦,搜索找到了一份。

一共 18 頁,一一展示一下。

有的地方是純知識點,有的地方是代碼。

反正我覺得我看明白了,有必要講一下的地方。我就在圖片下面進行一個簡短的描述。

走起。

稍微解釋一下這一頁上面的東西。

這幾個命令,我在之前的文章中也用到過:

一個困擾我122天的技術問題,我好像知道答案了。

就是下面這部分:

所以,我知道這個地方是有個坑的。

如果你把命令直接粘過去,會拋出這個錯誤:

Java HotSpot(TM) 64-Bit Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output

是因爲你缺少了一個叫做 hsdis-amd64.dll 的文件(windows平臺)。

你需要把這個文件放在 jre/bin/server 的目錄下:

然後把命令換成這個:

-XX:+UnlockDiagnosticVMOptions
-XX:+PrintAssembly
-XX:CompileCommand=print,*AtomicInteger.incrementAndGet

再次執行程序,就有彙編輸出了:

再主要說一下 CompileCommand 這個參數。

這個參數用來定製編譯需求,比如可以指定某個方法不做 JIT 編譯,也可以只編譯指定的方法等等。

用法是這樣的:

-XX:CompileCommand=command,method[,option]

其中的 command 有下面這些選項:

  • exclude,跳過編譯指定的方法
  • compileonly,只編譯指定的方法
  • inline/dontinline,設置是否內聯指定方法
  • print,打印生成的彙編代碼
  • break,JVM以debug模式運行時,在方法編譯開始處設置斷點
  • quiet,不打印在此命令之後、通過-XX:CompileCommand指定的編譯選項
  • log,記錄指定方法的編譯日誌,若未指定,則記錄所有方法的編譯日誌
  • 其他命令,option,help

比如 PPT 中的這個用法 -XX:CompileCommand=print,*AtomicInteger.incrementAndGet

含有就是打印 AtomicInteger.incrementAndGet 方法生成的彙編代碼。

緩衝行對齊,這個應該是一個需要掌握的知識點,屬於關於程序優化的奇技淫巧。

偶現於面試環節。

對齊的目的是爲了解決僞共享(False Sharing)的發生。

不知道僞共享的朋友,建議去了解一下。

PPT 中的例子的源碼來源是:

com.google.code.yanf4j.util.LinkedTransferQueue.PaddedAtomicReference

需要注意的是,在 Java8 裏面,提供了更加優雅的解決方案:@Contented 註解。

這個也是一個非常經典的、關於單例模式的演變過程。

PPT 上一共四個單例模式的寫法,我們一個個的看。

這就是我們耳熟能詳的餓漢式單例。通過提前初始化的方式,解決了併發問題,保證了單例性。

這裏面最關鍵的就是 final static 關鍵字。

作者也用了藍色做以區分,肯定是有深意的。

來,一起背一遍類的加載過程:

加載、驗證、準備、解析、初始化。

比如下面這個例子:

public static int value =123

變量 value,只有 static 修飾,那麼該變量在準備階段被賦上初始值,即 0。

在初始化階段被賦上 123。

但是當再多一個 final 修飾的時候:

public final static int value =123

value 變量在準備階段就會被賦上 123。

所以,你想想上面的餓漢式,是不是一個道理?

而這一方面的知識點,在《深入理解Java虛擬機》一書裏面,寫的還是很清楚的。

但是餓漢式有一個明顯的劣勢,那就是不管應用程序是否使用,都會被初始化出來。

這樣並不優雅。

於是,就到了第二段程序,飽漢式。

在需要使用的時候,才進行初始化操作。

但是怎麼保證線程安全呢?

通過在方法上加 synchronized 關鍵字的方式來保證了線程安全?

但是這個玩意真的安全嗎?

這可能是面試官最喜歡問的問題之一吧。

要是你之前不知道,也許打死你也想不到還有拿到一個只初始化了一半的對象的情況。

簡單來說就是 new Singleton() 不是原子性的,所以會導致 if 條件滿足時,instance 並沒有完全初始化。

於是就引出了下面這段程序:

傳說中的雙重檢查加鎖。

而這段程序最容易被“忽視”的就是 volatile 關鍵字了。

如果沒有 volatile 關鍵字,那麼這個程序也是廢的。

還有,需要注意的是梁飛在這裏備註了一個 jdk1.5+

爲什麼呢?

其實答案就寫在《Java併發編程實戰》的第 287 頁:

在這本書裏面,形容雙重檢查加鎖的成語是:聲名狼藉。

而且在圖片裏面可以看到:促使該模式出現的驅動力已經不復存在,並不是一種高效的優化措施。

現在,我覺得雙重檢查加鎖主要還是應用在面試環節。

明明就不是一種高效的優化措施,卻還是一個高頻考點,爲什麼呢?

還不是八股文害人啊。

接下來說最後一種,也是作者推崇的方式:

這個騷操作的學名叫做“基於類初始化的單例模式解決方案”。

JVM 在 Class 被加載後,且被線程使用之前,也就是類的初始化階段,會去獲取一個鎖。

這個鎖可以保證多個線程對同一個類進行初始化時,只有一個線程能初始化成功。

而該方法的原理在《Java併發編程的藝術》一書的 3.8.4 章節解釋的非常清楚。

什麼?這兩本書你都沒有?

趕緊去搞一本吧,寫的還是挺好的,我以前多次推薦過。

額......

這頁我也沒看明白。

我覺得肯定是有一個上下文的,只是梁飛沒有寫在 PPT 裏面。

導致我看的雲裏霧裏的。

這裏只寫到了 CountDownLatch 和 CyclicBarrier 。

其實還有一個很重要的、很常用的、工具類:Semaphore。

如果不知道,可以看看這篇文章:

我靠!Semaphore裏面居然有這麼一個大坑!

如果你上面三個都不知道。

趕!緊!去!學!

打印出來,貼在電腦旁邊,朗讀並背誦本頁。

最後說一句

文中提到的兩個 PPT 和一個文件:

  • 阿里巴巴(B2B)的服務框架探索.ppt
  • Java併發編程常識.ppt
  • hsdis-amd64.dll

關注公衆號【why技術】,然後在後臺回覆關鍵字【ppt】即可獲得。

才疏學淺,難免會有紕漏,如果你發現了錯誤的地方,可以在後臺提出來,我對其加以修改。 感謝您的閱讀,我堅持原創,十分歡迎並感謝您的關注。

我是 why,一個主要寫代碼,經常寫文章,偶爾拍視頻的程序猿。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章