【線程池】 正確創建線程池的方法

正確創建線程池的方法

線程池可以通過Executors工具類自動創建,也可以手動創建。對於如何選擇,《Java開發手冊》有如下描述:

線程池不允許使用Executors創建,而是通過ThreadPoolExecutor方式。這樣可以使開發人員更加明確線程池的運行規則,規避資源耗盡的風險。
Executros生成線程池對象的弊端:

  • FixedThreadPool和SingleThreadPool:允許的請求隊列長度爲Integer.MAX_VALUE,可能堆積大量請求,導致OOM。
  • CachedThreadPool允許創建的線程數量爲Integer.MAX_VALUE,可能創建大量的線程,導致OOM。
    在這裏插入圖片描述

FixedThreadPool內存溢出的示例

FixedThreadPool採用無界隊列,容量上限是Integer.MAX_VALUE,當請求越來越多,並且無法及時處理完畢時,就會佔用大量內存,導致OOM。

public class FixedThreadPoolOOM {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        for(int i = 0; i < Integer.MAX_VALUE; i++) {
            System.out.println(i);
            pool.execute(new Task());
        }
    }
}

class Task implements Runnable{

    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

爲了能夠儘快內存溢出,調整JVM參數如下

-Xms8M -Xmx8M

運行結果:內存溢出時,工作隊列中有164963個任務。

164963
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.lang.Integer.toString(Integer.java:403)
	at java.lang.String.valueOf(String.java:3099)
	at java.io.PrintStream.print(PrintStream.java:597)
	at java.io.PrintStream.println(PrintStream.java:736)
	at com.lzp.java.concurrent.threadpool.FixedThreadPoolOOM.main(FixedThreadPoolOOM.java:19)

核心線程數的計算

根據不同的業務場景,自己設置線程池參數,例如內存多大,線程名字等。

  • CPU密集型任務(加密、計算hash等):最佳線程數爲CPU核心數的1-2倍。
  • 耗時IO型(讀寫數據庫、文件、網絡讀寫等):最佳線程數一般大於CPU核心數很多倍,以JVM線程監控顯示繁忙情況爲依據,保證線程空閒可以銜接上。估算公式爲
線程數 = CPU核心數 * (1 + 平均等待時間 / 平均工作時間)

如果需要更加精準的數值,則需要進行壓測。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章