線上服務中有個使用 Spring 的scheduling.quartz的定時任務,每天凌晨1點向表里面灌初始數據,一個線程一台服務器在跑,剛上線的時候4個小時跑完,后來隨着用戶的激增,要下下午6點才結束,后來同事做了多線程優化,效果不顯著,耗時依舊嚴重,Zzzzzzz~~~~
於是clone 代碼看了下,感覺ExecutorService的使用怪怪的:
// 10個線程,因為任務多,這里用LinkedBlockingQueue private static final LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); private static final ExecutorService service = new ThreadPoolExecutor(0, 10, 60L, TimeUnit.SECONDS, queue );
ExecutorService 的參數定義:
1.當線程池小於corePoolSize時,新提交任務將創建一個新線程執行任務,即使此時線程池中存在空閑線程。
2.當線程池達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行
3.當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會創建新線程執行任務
4.當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理
5.當線程池中超過corePoolSize線程,空閑時間達到keepAliveTime時,關閉空閑線程
容易產生錯誤的理解:
代碼中的corePoolSize=0,也就是核心線程數是1,如果任務數多於10個,那么會先創建maximumPoolSize個線程執行,其余的任務加入 queue 中等待執行。
再看下類ThreadPoolExecutor中的注釋:
* When a new task is submitted in method {@link #execute}, and fewer * than corePoolSize threads are running, a new thread is created to * handle the request, even if other worker threads are idle. If * there are more than corePoolSize but less than maximumPoolSize * threads running, a new thread will be created only if the queue is * full.
注意紅色部分,而我們代碼中的 queue 是個類似無界的LinkedBlockingQueue(其實是有界的,SIZE:Integer.MAX_VALUE)
queue 是不會 full 的,那么永遠不會有maximumPoolSize個線程被創建,也就是說我們的任務一直還是一個線程在跑.......
果然只是把這個數字corePoolSize改為了3,耗時瞬間下降到了6、7個小時。又加了一台機器,依照業務主鍵的 ID 分布式跑任務,初始數據耗時縮減至3個小時左右!