Java併發編程系列——線程池

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之前寫了線程和鎖,例子中採用直接創建線程的方式,這種方式做示例可以,但在實際生產環境中比較少用,通常會使用線程池。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用線程池有一些明顯的好處,可以考慮我們使用連接池的情形,不難想像。使用線程池可以免去我們手動創建和銷燬線程的工作,節省這部分資源的消耗,提高響應速度,同時線程由線程池維護,也提高了線程的可管理性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK中默認實現了多種線程池,如FixedThreadPool,SingleThreadExecutor,CachedThreadPool,ScheduledThreadPool,SingleThreadScheduledExecutor,WorkStealingPool。ForkJoinPool也是線程池的一種,通常我們單獨討論,之前的文章有所介紹。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"線程工廠"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程池的創建方法使用線程池的工廠類Executors,調用相應的方法創建相應的線程池。創建線程池的工作即實例化ThreadPoolExecutor,所以有必要簡單看下ThreadPoolExecutor。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ThreadPoolExecutor的構造參數:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n* Creates a new {@code ThreadPoolExecutor} with the given initial\n* parameters.\n*\n* @param corePoolSize the number of threads to keep in the pool, even\n* if they are idle, unless {@code allowCoreThreadTimeOut} is set\n* @param maximumPoolSize the maximum number of threads to allow in the\n* pool\n* @param keepAliveTime when the number of threads is greater than\n* the core, this is the maximum time that excess idle threads\n* will wait for new tasks before terminating.\n* @param unit the time unit for the {@code keepAliveTime} argument\n* @param workQueue the queue to use for holding tasks before they are\n* executed. This queue will hold only the {@code Runnable}\n* tasks submitted by the {@code execute} method.\n* @param threadFactory the factory to use when the executor\n* creates a new thread\n* @param handler the handler to use when execution is blocked\n* because the thread bounds and queue capacities are reached\n* @throws IllegalArgumentException if one of the following holds:
\n* {@code corePoolSize < 0}
\n* {@code keepAliveTime < 0}
\n* {@code maximumPoolSize <= 0}
\n* {@code maximumPoolSize < corePoolSize}\n* @throws NullPointerException if {@code workQueue}\n* or {@code threadFactory} or {@code handler} is null\n*/\npublic ThreadPoolExecutor(int corePoolSize,\n int maximumPoolSize,\n long keepAliveTime,\n TimeUnit unit,\n BlockingQueue workQueue,\n ThreadFactory threadFactory,\n RejectedExecutionHandler handler) "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline"},{"type":"codeinline"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對如上參數簡單說明:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"corePoolSize:線程池中的核心線程數"},{"type":"text","text":"。也就是線程池會長期保持的線程數。當通過線程池執行任務時,如果當前的線程數小於corePoolSize,則創建新線程,如果當前線程數已經爲corePoolSize,則任務進入工作隊列。如果希望一次性創建出核心線程,調用prestartAllCoreThreads()。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"maximumPoolSize:池中允許的最大線程數"},{"type":"text","text":"。當核心線程數滿,並且阻塞隊列也滿了,此時如果池中的線程數仍小於maximumPoolSize,則會創建新的線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"keepAliveTime:空閒線程存活的時間"},{"type":"text","text":"。僅當池中線程數大於corePoolSize時有效,也就是在上述maximumPoolSize所講條件觸發創建線程後,使得池中線程大於核心線程數後,纔會根據該條件來銷燬線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"unit:時間單位"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"workQueue:保存尚未執行的任務的阻塞隊列"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"threadFactory:創建線程的工廠"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"handler:飽和策略"},{"type":"text","text":"。也就是阻塞隊列滿了之後的處理方式。飽和策略有四種,AbortPolicy(拋出異常),CallerRunsPolicy(由調用線程直接執行,如果 調用線程已經銷燬則丟棄),DiscardOldestPolicy(丟棄最早的任務,即隊列頭部的任務),DiscardPolicy(直接丟棄)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"任務的執行"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用線程池執行任務有兩種方式,"},{"type":"text","marks":[{"type":"italic"}],"text":"execute和submit"},{"type":"text","text":",execute無返回值,submit返回Future,而Future可以返回結果和接收異常。根據需要使用具體的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"線程池的停止"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關閉線程池使用"},{"type":"text","marks":[{"type":"italic"}],"text":"shutdown()和shutdownNow()"},{"type":"text","text":"。使用shutdown()時,尚未被執行的任務將不再執行,而已經在執行的任務將繼續。使用shutdownNow()時,尚未被執行的任務將不再執行,並且會嘗試停止正在運行的任務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來簡單介紹下"},{"type":"text","marks":[{"type":"strong"}],"text":"幾個常見線程池"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"FixedThreadPool:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"corePoolSize等於maximumPoolSize,阻塞隊列使用了LinkedBlockingQueue,但並未初始化其容量,可以認爲相當於使用了無界隊列("},{"type":"text","marks":[{"type":"strong"}],"text":"爲什麼說到無界,文章最後會提到"},{"type":"text","text":")。適用於對服務器負載有嚴格控制的場景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"SingleThreadExecutor:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"corePoolSize等於maximumPoolSize等於1,阻塞隊列同樣使用了未初始化容量的LinkedBlockingQueue,爲無界隊列。適用於對任務執行順序有要求的場景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"CachedThreadPool:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"corePoolSize爲0,maximumPoolSize爲Integer.MAX_VALUE,使用的隊列爲SynchronousQueue,同樣爲無界。該線程池會根據需要創建新線程,適用於執行時間非常短的數量較多的異步任務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"ScheduledThreadPool:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其maximumPoolSize爲Integer.MAX_VALUE,隊列使用了DelayedWorkQueue,同樣爲無界。適用於需要定期執行任務的場景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"SingleThreadScheduledExecutor"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"corePoolSize爲1,隊列同樣使用了DelayedWorkQueue,爲無界。適用於需要定期按順序執行任務的場景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"WorkStealingPool:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"工作密取隊列,內部使用了ForkJoinPool,但使用了默認工廠創建,同樣爲無界形式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"線程池使用示例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過一段代碼簡單看下線程的使用,以SingleThreadExecutor爲例。"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ShowSingleExecutor {\n public static void main(String[] args) {\n ExecutorService executorService = Executors.newSingleThreadExecutor();\n executorService.execute(() -> {\n System.out.println(\"executed 1 by single thread\");\n try {\n Thread.sleep(5000);\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n });\n\n executorService.execute(() -> {\n System.out.println(\"executed 2 by single thread\");\n }\n );\n executorService.shutdown();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該示例將演示使用單線程線程池,其第一個任務執行5秒後纔會執行第二個任務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其他線程池文章中鑑於篇幅不再舉例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"自定義線程池"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後說一下之前提到的無界問題。無界意味着如果出現處理不夠及時的情況時,任務會逐漸堆積,而造成服務不可用或服務崩潰。所以在實際使用中通常需要自定義ThreadPoolExecutor,並在內部使用有界隊列的方式或通過其他手段達到類似有界的效果。對於隊列滿時的飽和策略除了文中介紹的四種實現,同樣可以根據實際情況自定義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本系列其他文章:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/25211725a8f651cccab028b84","title":""},"content":[{"type":"text","text":"Java併發編程系列——鎖順序"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/9b34fc0675a371ab84ab4dfbd","title":null},"content":[{"type":"text","text":"Java併發編程系列——鎖"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/96d4f3c1f8308f3d2adaad798","title":null},"content":[{"type":"text","text":"Java併發編程系列——常用併發工具類"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/6a9f9c343f8f4ec6f1a3f2616","title":null},"content":[{"type":"text","text":"Java併發編程系列——Fork-Join"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/a247613355c8284b6f0ba892b","title":null},"content":[{"type":"text","text":"Java併發編程系列——線程的等待與喚醒"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/48cccd6b51b97d2abdcad0e2b","title":null},"content":[{"type":"text","text":"Java併發編程系列插曲——對象的內存結構"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/fdb0af09a682ef3012f6f46c5","title":null},"content":[{"type":"text","text":"Java併發編程系列——線程"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章