多線程編程雖能提升吞吐量,但不當使用反而拖垮性能。本文總結六大經驗技巧,幫你避開常見坑點,寫出高效穩健的並發代碼。
痛點:頻繁創建/銷毀線程開銷大,且無限制創建會導致 OOM。
方案:使用 ExecutorService 線程池復用線程,控制併發數,提升響應速度。
推薦:ThreadPoolExecutor 自定義核心參數,或 Executors 工廠方法快速創建。
原則:同步塊越小越好。
避坑:
• 單線程環境下完全不需要同步
• 能用非同步數據結構(如 ThreadLocal、StringBuilder)就不用同步
• 將不需要同步的代碼移出 synchronized 塊
錯誤示範:
public void bad() {
synchronized(this) {
// 大量非共享邏輯
doSomething();
// 只有這行需要同步
sharedCount++;
}
}
正確做法:
public void good() {
doSomething(); // 非共享操作移出
synchronized(this) {
sharedCount++; // 僅同步關鍵行
}
}
原因:Vector/Hashtable 所有方法皆同步,性能損耗大。
替代方案:
• 單線程或外部同步:ArrayList、HashMap
• 高併發寫入:CopyOnWriteArrayList、ConcurrentHashMap
數據:非同步集合性能通常提升 2-10 倍。
notifyAll() 會喚醒所有等待線程,引發「驚群效應」,導致大量線程競爭鎖、上下文切換。
若確定只需喚醒一個線程(如生產者消費者單消費者),用 notify() 精準喚醒,大幅降低競爭開銷。
錯誤模式:
for (int i=0; i<1000; i++) {
synchronizedMethod(); // 每次進出鎖,開銷巨大
}
優化:將鎖外移至循環外(若業務允許),或使用細粒度鎖、無鎖數據結構、LongAdder 等原子類。
| 技巧 | 核心原則 | 適用場景 |
|---|---|---|
| 線程池 | 復用線程,控制併發 | 高併發短任務 |
| 減少同步 | 能不鎖就不鎖 | 單線程、ThreadLocal |
| 縮小鎖粒度 | 只鎖共享變量操作 | 混合邏輯方法 |
| 非同步集合 | 去除內置同步開銷 | 單線程/外部同步 |
| notify 精準喚醒 | 避免驚群效應 | 單消費者隊列 |
| 循環外加鎖 | 減少鎖進出次數 | 批量處理共享數據 |
掌握這六大技巧,配合 JUC 包(java.util.concurrent)工具類,能解決 90% 以上的線程性能問題。