在手寫線程池時,通常選擇使用 有界隊列 或 無界隊列,具體選擇哪一種取決於具體的應用場景和需求。下面是每種隊列的優缺點,並解釋為什麼在手寫線程池時通常選擇其中的某些隊列。
1. 有界隊列(ArrayBlockingQueue)
- 使用場景:對於大多數場景,尤其是當任務數量較為穩定或可控時,有界隊列 是一個較好的選擇。它通常用於大多數生產環境中的線程池設計中。
-
為什麼使用:有界隊列的最大優勢是可以限制線程池中任務的數量,防止任務積壓造成內存耗盡或系統崩潰。
- 例如:假設我們設定了最大線程數(比如 10 個線程),同時設定隊列容量(比如 100 個任務)。當隊列滿時,線程池會拒絕新的任務或者阻塞提交任務的線程,直到隊列有空閒位置。這種機制有效避免了內存爆炸和資源超載。
- 優點:有界隊列能有效防止任務數量過多導致內存泄漏等問題,保證系統的穩定性,能夠幫助我們實現合理的負載控制。
- 缺點:如果任務提交速度過快,隊列滿了,後續任務可能會被拒絕或需要長時間阻塞。
常見隊列選擇:
ArrayBlockingQueue代碼示例:
ExecutorService executorService = new ThreadPoolExecutor( 4, // corePoolSize 10, // maximumPoolSize 60L, TimeUnit.SECONDS, // keepAliveTime new ArrayBlockingQueue<>(100), // 隊列 new ThreadPoolExecutor.AbortPolicy() // 拒絕策略 );
2. 無界隊列(LinkedBlockingQueue)
- 使用場景:如果任務提交和處理的速度相對均衡,且我們不關心隊列是否會過多積壓任務,無界隊列 也是一個常見的選擇。
-
為什麼使用:無界隊列能夠承接無限量的任務,避免了因任務隊列大小限制而導致的拒絕或阻塞問題。在流量較大時可以避免線程池因隊列滿而拒絕任務。
- 優點:任務可以無限制地加入隊列,線程池的核心線程池數和最大線程數不需要頻繁調整,適應性較強。
- 缺點:如果任務提交速率過高,隊列中的任務會持續增加,可能導致內存消耗過多,甚至出現 OutOfMemoryError。另外,線程池中的線程數也會不斷增加,導致線程數過多時會有額外的上下文切換開銷。
常見隊列選擇:
LinkedBlockingQueue代碼示例:
ExecutorService executorService = new ThreadPoolExecutor( 4, // corePoolSize 10, // maximumPoolSize 60L, TimeUnit.SECONDS, // keepAliveTime new LinkedBlockingQueue<>() // 無界隊列 );
3. 同步隊列(SynchronousQueue)
- 使用場景:這種隊列適用於需要快速處理每個任務的場景,通常任務處理時間較短且每個任務都需要獨立的線程執行。
-
為什麼使用:
SynchronousQueue是一個不緩存任何任務的隊列,它會直接將任務交給線程池中的線程來執行,如果沒有線程空閒,提交任務的線程將被阻塞,直到有線程來處理任務。- 優點:沒有任務積壓,適合需要高併發且任務時間短的場景,每個任務都會被迅速處理。
- 缺點:沒有緩存任務,可能導致頻繁創建和銷燬線程,影響性能。
常見隊列選擇:
SynchronousQueue代碼示例:
ExecutorService executorService = new ThreadPoolExecutor( 4, // corePoolSize 10, // maximumPoolSize 60L, TimeUnit.SECONDS, // keepAliveTime new SynchronousQueue<>() // 同步隊列 );
總結:
- 有界隊列(
ArrayBlockingQueue):常用,因為它可以有效控制任務的數量,避免系統因為任務過多而崩潰。適用於任務量大且可控的場景。 - 無界隊列(
LinkedBlockingQueue):適合任務數目不確定且任務提交速率較低的情況,雖然它不會拒絕任務,但需要小心內存消耗。 - 同步隊列(
SynchronousQueue):適合極短時間、獨立線程執行的任務場景,常用於高併發、需要快速響應的場景。
因此在手寫線程池時,通常會選擇 ArrayBlockingQueue 或 LinkedBlockingQueue,具體根據任務的性質和系統的負載來決定:
- 如果任務提交量較大且能控制系統負載,通常選擇
ArrayBlockingQueue,它能避免系統崩潰。 - 如果任務不太頻繁且可以容忍一些任務積壓,使用
LinkedBlockingQueue是一個不錯的選擇。