這是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 頁,一一展示一下。
有的地方是純知識點,有的地方是代碼。
反正我覺得我看明白了,有必要講一下的地方。我就在圖片下面進行一個簡短的描述。
走起。
稍微解釋一下這一頁上面的東西。
這幾個命令,我在之前的文章中也用到過:
就是下面這部分:
所以,我知道這個地方是有個坑的。
如果你把命令直接粘過去,會拋出這個錯誤:
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。
如果不知道,可以看看這篇文章:
如果你上面三個都不知道。
趕!緊!去!學!
打印出來,貼在電腦旁邊,朗讀並背誦本頁。
最後說一句
文中提到的兩個 PPT 和一個文件:
阿里巴巴(B2B)的服務框架探索.ppt Java併發編程常識.ppt hsdis-amd64.dll
關注公衆號【why技術】,然後在後臺回覆關鍵字【ppt】即可獲得。
才疏學淺,難免會有紕漏,如果你發現了錯誤的地方,可以在後臺提出來,我對其加以修改。 感謝您的閱讀,我堅持原創,十分歡迎並感謝您的關注。
我是 why,一個主要寫代碼,經常寫文章,偶爾拍視頻的程序猿。