《阿里巴巴Java工作手冊》學習筆記


最近瀏覽了一下阿里巴巴的Java開發手冊,感覺內容確實非常的贊,發現了不少自己在編程中的誤區,因此決定通過成文牢固掌握,文中將選取個人認為比較重要的部分進行描述與分析。”願站在巨人的肩膀上,碼出高效,碼出質量“

概述

手冊中內容很多,包括編碼規約、工程結構、MySQL數據庫、異常日志、安全規約、單元測試等6大部分。其中工程結構部分在整體上對項目進行了把控,編碼規約和MySQL設計規約中有非常多的“最佳實踐”,個人認為是最為有價值的部分。次重點安全規約很符合互聯網場景的需要,異常日志和單元測試部分相對來說內容和價值相對小一點。
下圖是個人對手冊的簡單匯總(將圖片拖到瀏覽器新開頁面,之后放大可以清晰很多),其中紅星表示重點,黃色表示次重點。此外,其中的集合處理、並發處理、MySQL規約和工程結構有一定理解難度,會專門放到重難點增強一節進行詳細分析。當然這只是個人的淺見,完整部分請見最下方的參考資料,目前阿里公司已推出這套規約的IDE插件,大家可以試試。

重難點增強

工程結構


Open Interface:提供RPC或Restful風格的接口,並通過框架進行網關安全控制、流量控制等。
 終端顯示層:前端模板渲染並執行顯示的層。
Web Layer: 主要是對訪問控制進行轉發, 各類基本參數校驗, 或者不復用的業務簡單處理等。
Service Layer:業務邏輯服務層。
Manager Layer:這一層比較有意思,既包含了常見的外部接口的Agent功能,也包含了對DAO接口的簡單封裝用於復用,還可以在改成構建對於DAO的緩存。
DAO Layer:與底層 MySQL、Hbase等進行數據交互,通常基於各類DAL框架。
ExternalInterface: 包括其它部門 RPC 開放接口, 基礎平台, 其它公司的 HTTP 接口。
Tip: 實際項目中完全可以遵循該模式構建包的層次

集合處理

集合操作一直是一個關鍵點,常見的小技巧包括通過Set元素唯一的特性,可以快速對一個集合進行去重操作,JD8中通過steam流對集合操作做了不錯的增強。合理利用好集合的有序性 (sort) 和穩定性 (order) ,避免集合的無序性 (unsort) 和
不穩定性 (unorder) 帶來的負面影響,ArrayList是order&unsort;HashMap是unorder&unsort;TreeSet 是order&sort。
TIp: 不同類型Map集合K/V能不能存儲null值的情況如下表所示

並發處理

合理的通過並發控制來高效利用系統資源是工程師永遠的追求,該手冊的不少要求很好的認識到自己在該方面的不足。
線程池的創建
過去一半都通過Executors 去創建線程池,實際上這是不合理的,比如FixedThreadPool的阻塞隊列大小Integer.MAX_VALUE,很容易造成大量請求的堆積,造成OOM。而ScheduledThreadPool允許線程的數量為Integer.MAX_VALUE,更加容易出現OOM。因此必須使用ThreadPoolExecutor創建線程池,明確的制定線程池的參數,詳情請見深入理解Java線程池一文
使用鎖的原則
1.高並發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖 ; 能鎖區塊,就不要鎖整個方法體 ; 能用對象鎖,就不要用類鎖。
2.對多個資源、數據庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造成死鎖。
3.並發修改同一記錄時,避免更新丟失,需要加鎖。要么在應用層加鎖,要么在緩存加鎖,要么在數據庫層使用樂觀鎖,使用 version 作為更新依據。如果每次訪問沖突概率小於 20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數不得小於 3 次
CountDownLatch
使用 CountDownLatch 進行異步轉同步操作,每個線程退出前必須調用 countDown方法,線程執行代碼注意 catch 異常,確保 countDown 方法被執行到,避免主線程無法執行至 await 方法,直到超時才返回結果。需要注意子線程拋出異常堆棧,不能在主線程的try-catch塊中捕獲到。
ThreadLocalRandom
避免 Random 實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed 導致的性能下降,推薦使用ThreadLocalRandom。
Volatile
volatile 解決多線程內存不可見問題。對於一寫多讀,是可以解決變量同步問題,但是如果多寫,同樣無法解決線程安全問題。在完成飽漢的單例模式時,可以使用Volatile關鍵字實現延遲初始化。
LongAdder
對於count++操作,可以使用AtomicInteger count = new AtomicInteger();,JDK8中推薦使用LongAdder,通過減少樂觀鎖的重試次數提高性能。
HashMap
HashMap 在容量不夠進行 resize 時由於高並發可能出現死鏈,導致 CPU 飆升,在開發過程中可以使用其它數據結構或加鎖來規避此風險。
ThreadLocal
ThreadLocal 對象建議使用 static修飾。這個變量是針對一個線程內所有操作共享的。

MYSQL規約

數據庫方面的優化一直困擾着我,書上的一下幾個原則給予個人在數據庫優化方面不少啟示。
索引長度
過去一直沒有注意過索引還有長度這個概念,現在才知道在varchar等類型字段上建立索引時,可以指定索引長度,達到一定區分度即可,一般對於字符串類型數據,長度為 20 的索引,區分度會高達 90%以上,可以使用count(distinct left( 列名, 索引長度 )) / count( * )的區分度來確定。

此外頁面搜索嚴禁左模糊或者全模糊,如果需要請走搜索引擎來解決。索引文件具有B-Tree的最左前綴匹配特性,如果左邊的值未確定,那么無法使用此索引。
order by
在有order by的場景,請注意利用索引的有序性。例如where a =? and b =? order by c的索引為a _ b _ c
覆蓋索引
利用覆蓋索引可以減少原表查詢,通過索引頁(目錄)即可查到所需信息。通常來說,能夠建立索引的種類包括主鍵索引、唯一索引、普通索引,而覆蓋索引是查詢的一種效果,可以通過explain查看到extra 列會出現using index

分頁查詢
MySQL並不是跳過offset行,而是取 offset + N 行,之后放棄offset行,返回所需N行,當數據量大時,推薦如下方式進行優化。先快速定位需要獲取的 id 段,然后再關聯:
SELECT a.* FROM 表 1 a, (select id from 表 1 where 條件 LIMIT 100000,20 ) b where a.id=b.id
SQL 性能優化的目標
至少要達到 range 級別, 要求是 ref 級別, 如果可以是 consts最好。
consts 單表中最多只有一個匹配行(主鍵或者唯一索引),在優化階段即可讀取到數據。
ref 指的是使用普通的索引 (normal index) 。
range 對索引進行范圍檢索。
tip: type = index,索引物理文件全掃描,速度非常慢,這個 index 級別比較 range 還低,與全表掃描是小巫見大巫。

在Mysql Workbench中表述稍微有些區別:consts沒變,Non-Unique Key Lookup表示ref, Index Range Scan表示range, full table Scan表示All, full index scan表示index,當然也可以選擇Tabular視圖替代Visual視圖。

組合索引
區分度最高的在最左邊,對於where a =? and b =?來說,a 列的值越唯一越好。此外,存在非等號和等號混合判斷條件時, 在建索引時, 請把等號條件的列前置。
隱式轉換
防止因字段類型不同造成的隱式轉換,導致索引失效,當數據查詢超時,也需要把此作為很重要的考慮部分。

參考資料
阿里巴巴Java開發手冊
阿里規約github


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM