1. 程式人生 > >阿里巴巴Java開發手冊重點解讀(一)

阿里巴巴Java開發手冊重點解讀(一)

程式設計規約-OOP規約-8

【強制】關於基本資料型別與包裝資料型別的使用標準如下:
1) 所有的 POJO 類屬性必須使用包裝資料型別。
2) RPC 方法的返回值和引數必須使用包裝資料型別。
3) 所有的區域性變數【推薦】 使用基本資料型別。

這些型別的物件常作為查詢物件或者結果物件,基本資料型別的預設值都是0,而引用型別的預設值為null。在拼接sql時或者顯示結果時,null有明確的含義,可以不拼接或者不顯示,但是0的含義有二義性,是沒有值還是真的為0?

程式設計規約-OOP規約-9

【強制】定義 DO/DTO/VO 等 POJO 類時,不要設定任何屬性預設值

同理。pojo作為查詢物件或者更新物件時,對根據條件自動拼接sql來說,是一種傷害。原文中已經解釋比較清楚了。

反例: POJO 類的 gmtCreate 預設值為 new Date();但是這個屬性在資料提取時並沒有置入具體值,在更新其它欄位時又附帶更新了此欄位,導致建立時間被修改成當前時間。

程式設計規約-併發處理-3&4

3【強制】執行緒資源必須通過執行緒池提供,不允許在應用中自行顯式建立執行緒。
說明: 使用執行緒池的好處是減少在建立和銷燬執行緒上所花的時間以及系統資源的開銷,解決資源不足的問題。如果不使用執行緒池,有可能造成系統建立大量同類執行緒而導致消耗完記憶體或者“過度切換”的問題。

也就是不允許new Thread(…).start()這種形式來建立和啟動執行緒。執行緒是稀缺資源,建立和銷燬都有代價。執行緒池將執行緒和任務分開,無論多少任務,都可以用可控制的執行緒條數來執行,執行緒得以被重用。同時,不會因為任務的暴增導致執行緒數的暴增,執行緒數的暴增會引起CPU過載,很有可能造成系統假死,甚至造成jvm外別的應用程式不響應。

4【強制】執行緒池不允許使用 Executors 去建立,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確執行緒池的執行規則,規避資源耗盡的風險。
說明: Executors 返回的執行緒池物件的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允許的請求佇列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2) CachedThreadPool 和 ScheduledThreadPool:
允許的建立執行緒數量為 Integer.MAX_VALUE, 可能會建立大量的執行緒,從而導致 OOM。

Executors是一個工具類,它有幾個便捷的靜態方法可以幫我們建立不同特性的執行緒池,但是這些執行緒池都有風險。這些便捷方法,其實都是幫我們定好了ThreadPoolExecutor的構造引數,返回結果也是ThreadPoolExecutor的例項。
ThreadPoolExecutor的構造引數需要精心設計,具體問題具體分析,才能滿足我們的需求,其引數含義是
int corePoolSize,核心執行緒數,執行緒池維護的最小執行緒數
int maximumPoolSize,最多執行緒數
long keepAliveTime,核心執行緒數之外建立的執行緒從空閒到消亡的等待時間
TimeUnit unit,上一個引數的時間單位
BlockingQueue workQueue,等待佇列,任務數太多,超出核心執行緒中可用執行緒,也就是核心執行緒滿足不了持續增長的任務時,新任務被放入此佇列,此佇列滿,將會擴大執行緒數,直到執行緒數達到max的限定
RejectedExecutionHandler handler,核心執行緒繁忙不能執行新任務,新任務進佇列,佇列滿,新增執行緒直到匯流排程數到達maximumPoolSize,所有執行緒都繁忙,這時還在提交新任務,就會呼叫這個拒絕handler來實施拒絕策略了。

需要注意的問題:
1. 如果等待佇列是無界佇列,那麼maximumPoolSize是不起作用的,後面的拒絕策略也是不起作用的,假設任務增長速度非常快,佇列具備無限擴容能力,記憶體很快就會被撐爆。這就是FixedThreadPool和SingleThreadPool的問題所在。
2. CachedThreadPool倒不是用無界佇列,它用了一個SynchronousQueue佇列,這個佇列就是個假佇列,不能放任何東西,你可以認為容量為0,那麼任何超出的任務都會導致新增執行緒,執行緒總數受max限制,然而它的上限被設為多少呢?Integer.MAX_VALUE。假設任務增長速度非常快,那麼這個執行緒池就會不停新建執行緒來處理新任務。這會導致CPU過載,和記憶體溢位。
3. ScheduledThreadPool類似。

一個良好設計的執行緒池是這樣的:
1. 核心數和平均任務數對應
2. 偶爾抖動,任務數增加,使用有界佇列(ArrayBlockingQueue設定大小)來緩衝任務
3. max不要太大,比核心數大一些就可以了,前面還有緩衝佇列呢。和核心數設定為一樣也可以
4. 時間的設計按預設的來就行
5. 可以有拒絕策略,為什麼不能呢?計算機資源是有限的,處理不了的時候丟擲異常總比撐爆CPU和記憶體要好。jdk提供了四種內建策略

a.AbortPolicy,丟擲RejectedExecutionException
b.CallerRunsPolicy,嘗試直接呼叫任務的run方法
c.DiscardOldestPolicy,將丟棄最老的等待中的任務,嘗試執行當前任務
d.DiscardPolicy,悄悄地丟棄,不拋任何異常

個人覺得,拋異常提示下任務提交方,是不錯的選擇。

下次我們聊聊其他問題。