## 六、并發處理
1. 【**強制**】獲取單例對象需要保證線程安全,其中的方法也要保證線程安全。
說明:資源驅動類、工具類、單例工廠類都需要注意。
* * * * *
2. 【**強制**】創建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。
正例:
<code>
public class TimerTaskThread extends Thread {
public TimerTaskThread() {
super.setName("TimerTaskThread"); ...
}
}
</code>
* * * * *
3. 【**強制**】線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。
說明:使用線程池的好處是減少在創建和銷毀線程上所花的時間以及系統資源的開銷,解決資 源不足的問題。如果不使用線程池,有可能造成系統創建大量同類線程而導致消耗完內存或者 “過度切換”的問題。
4. 【**強制**】線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。 說明:Executors 返回的線程池對象的弊端如下:
1). FixedThreadPool和SingleThreadPool:
允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2). CachedThreadPool 和 ScheduledThreadPool:
允許的創建線程數量為 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
* * * * *
5. 【**強制**】SimpleDateFormat 是線程不安全的類,一般不要定義為static變量,如果定義為static,必須加鎖,或者使用 DateUtils 工具類。
>正例:注意線程安全,使用 DateUtils。亦推薦如下處理:
<code>
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
</code>
說明:如果是 JDK8 的應用,可以使用 ```Instant``` 代替 ```Date```,```LocalDateTime``` 代替```Calendar```, ```DateTimeFormatter``` 代替 ```Simpledateformatter```,官方給出的解釋:simple beautiful strong immutable thread-safe。
* * * * *
6. 【**強制**】高并發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能 鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。
* * * * *
7. 【**強制**】對多個資源、數據庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造 成死鎖。
>說明:線程一需要對表 A、B、C 依次全部加鎖后才可以進行更新操作,那么線程二的加鎖順序 也必須是 A、B、C,否則可能出現死鎖。
* * * * *
8. 【**強制**】并發修改同一記錄時,避免更新丟失,需要加鎖。要么在應用層加鎖,要么在緩存加 鎖,要么在數據庫層使用樂觀鎖,使用 version 作為更新依據。 說明:如果每次訪問沖突概率小于 20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次 數不得小于 3 次。
* * * * *
9. 【**強制**】多線程并行處理定時任務時,```Timer``` 運行多個 ```TimeTask``` 時,只要其中之一沒有捕獲 拋出的異常,其它任務便會自動終止運行,使用 ```ScheduledExecutorService``` 則沒有這個問題。
* * * * *
10. 【推薦】使用 ```CountDownLatch``` 進行異步轉同步操作,每個線程退出前必須調用 countDown方法,線程執行代碼注意 ```catch 異常```,確保 ```countDown``` 方法可以執行,避免主線程無法執行 至 ```await``` 方法,直到超時才返回結果。
>說明:注意,子線程拋出異常堆棧,不能在主線程 try-catch 到。
* * * * *
11. 【推薦】避免 ```Random``` 實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一 seed 導致的性能下降。
>說明:Random 實例包括 java.util.Random 的實例或者 Math.random()實例。
>正例:在 JDK7 之后,可以直接使用 API ThreadLocalRandom,在 JDK7 之前,可以做到每個 線程一個實例。
* * * * *
12. 【推薦】在并發場景下,通過雙重檢查鎖(double-checked locking)實現延遲初始化的優 化問題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問 題解決方案中較為簡單一種(適用于 JDK5 及以上版本),將目標屬性聲明為```volatile``` 型。
反例:
<code>
class Foo {
private Helper helper = null; public Helper getHelper() {
if (helper == null) synchronized(this) { if (helper == null)
helper = new Helper();
}
return helper; }
// other functions and members...
}
</code>
* * * * *
13. 【參考】```volatile``` 解決多線程內存不可見問題。對于一寫多讀,是可以解決變量同步問題, 但是如果多寫,同樣無法解決線程安全問題。如果是 ```count++```操作,使用如下類實現: ```AtomicInteger count = new AtomicInteger(); count.addAndGet(1);``` 如果是 JDK8,推 薦使用 ```LongAdder``` 對象,比 ```AtomicLong``` 性能更好(減少樂觀鎖的重試次數)。
* * * * *
14. 【參考】 ```HashMap``` 在容量不夠進行 resize 時由于高并發可能出現死鏈,導致 CPU 飆升,在 開發過程中注意規避此風險。
* * * * *
15. 【參考】```ThreadLocal``` 無法解決共享對象的更新問題,```ThreadLocal``` 對象建議使用 ```static``` 修飾。這個變量是針對一個線程內所有操作共有的,所以設置為靜態變量,所有此類實例共享 此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,所有此類的對象(只 要是這個線程內定義的)都可以操控這個變量。
- 說明
- Python編程規范
- Python風格規范
- Python語言規范
- Java編程規范
- 一、命名約定
- 二、常量定義
- 三、格式約定
- 四、OOP約定
- 五、集合處理
- 六、并發控制
- 七、控制語句
- 八、注釋約定
- 九、異常日志
- 十、日志約定
- Android開發規范
- 前端開發規范
- HTML
- JavaScript
- CSS
- MySQL約定
- 一、基本規范
- 二、庫表設計規范
- 三、字段設計規范
- 四、索引規范
- 五、SQL設計規范
- 六、業務字段命名規范
- 開發安全約定
- 一、代碼安全
- 二、移動開發安全
- 三、服務器安全
- 四、安全意識
- 版本管理
- Git使用規范
- 技術實踐及可視化
- 一、Code Review
- 二、單元測試
- 三、自動化測試
- 四、技術債
- 五、CI
- IOS開發規范