快速掌握併發編程---線程池的原理和實戰

{"type":"doc","content":[{"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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/62/62e28e9646c372266efd8aebab403234.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"流行池化技術,那麼到底什麼是池化技術呢?"}]},{"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":"在編程領域,比較典型的池化技術有:"},{"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","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}},{"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":"blockquote","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":"還拿上邊的例子說,如果我們使用線程池的方式的話,可以實現指定線程的數量,這樣的話就算再多的數據需要入庫,只需要排隊等待線程池的線程即可,也就不用一直不斷的創建線程銷燬線程,就不會出現線程池過多而消耗系統資源的情況,當然這只是意見簡單的場景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/61/61ea2971a7b179106c201a9669e4a398.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"雖然如此,線程是一種輕量級的工具(或者稱之爲:輕量級進程),但其創建和關閉依然需要花費時間,如果爲了一個很簡單的任務就去創建一個線程,很有可能出現創建和銷燬線程所佔用的時間大於該線程真實工作所消耗的時間,反而得不償失。"}]},{"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}},{"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":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5a/5aeca865f02398fc0471356e4660cce8.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"簡而言之:創建線程變成了從線程池獲取空閒的線程,關閉線程變成了向池子中歸還線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"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":"Java 中的線程池是運用場景最多的併發框架,幾乎所有需要異步或併發執行任務的程序都可以使用線程池,Spring、Dubbo、Mybatis等等框架中都有大量的使用線程池。"}]},{"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":"在開發過程中,合理地使用線程池能夠帶來幾個好處:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"降低系統資源消耗,通過重用已存在的線程,降低線程創建和銷燬造成的消耗;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"提高系統響應速度,當有任務到達時,通過複用已存在的線程,無需等待新線程的創建便能立即執行;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"方便線程併發數的管控。因爲線程若是無限制的創建,可能會導致內存佔用過多而產生OOM(Out Of Memory),並且會造成CPU過度切換(CPU切換線程是有時間成本的,需要保持當前執行線程的現場,並恢復要執行線程的現場)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"提供更強大的功能,延時定時線程池。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7c/7cf8b9ce2919149629f16fc8bdde2594.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"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":"線程池將線程和任務進行解耦,線程是線程,任務是任務,擺脫了之前通過 Thread 創建線程時的一個線程必須對應一個任務的限制。"}]},{"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":"其核心原理在於線程池對 Thread 進行了封裝,並不是每次執行任務都會調用 Thread.start() 來創建新線程,而是讓每個線程去執行一個“循環任務”,在這個“循環任務”中不停的檢查是否有任務需要被執行,如果有則直接執行。"}]},{"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":"也就是調用任務中的 run 方法,將 run 方法當成一個普通的方法執行,通過這種方式將只使用固定的線程就將所有任務的 run 方法串聯起來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"JDK 自帶線程池"}]},{"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 提供了 java.util.concurrent.Executor接口,"}]},{"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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public interface Executor {\n    void execute(Runnable command);\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":"看看Executor實現類就知道,線程池使用的地方是相當多,netty、Spring、Google等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6d/6df14ddf3dbea2daf0b6ca16dd470486.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"但是我們這篇講的是Executor 的子接口ExecutorService"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public interface ExecutorService extends Executor {\n    void shutdown();\n    List shutdownNow();\n    boolean isShutdown();    \n    boolean isTerminated();   \n    boolean awaitTermination(long timeout, TimeUnit unit)  throws InterruptedException;   \n     Future submit(Callable task);    \n     Future submit(Runnable task, T result);    \n    Future> submit(Runnable task);\n     List> invokeAll(Collection extends Callable> tasks)   throws InterruptedException;\n     List> invokeAll(Collection extends Callable> tasks,long timeout, TimeUnit unit)\n        throws InterruptedException; \n     T invokeAny(Collection extends Callable> tasks) throws InterruptedException, ExecutionException;\n     T invokeAny(Collection extends Callable> tasks,long timeout, TimeUnit unit)\n        throws InterruptedException, ExecutionException, TimeoutException;\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":"相關類繼承關係如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3a/3aff3cb66587a171d185240fbf5b7f12.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"如果使用 Executor 框架的話,JDK提供了一個類似於工廠類的Executors 類,Executors 相比前面前面的Executor多了個s,這個得注意,Executors 其方法如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/25/25393aabcbd454d39926a0a0e983fc71.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"//該方法返回一個固定線程數量的線程池;\npublic static ExecutorService newFixedThreadPool()\n//該方法返回一個只有一個現成的線程池\npublic static ExecutorService newSingleThreadExecutor()\n//返回一個可以根據實際情況調整線程數量的線程池\npublic static ExecutorService newCachedThreadPool()\n//該方法和 newSingleThreadExecutor 的區別是給定了時間執行某任務的功能,可以進行定時執行等\npublic static ScheduledExecutorService newSingleThreadScheduledExecutor()\n//在newSingleThreadScheduledExecutor的基礎上可以指定線程數量\npublic static ScheduledExecutorService newScheduledThreadPool()\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"構造方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Executors 類中,上面幾個創建線程池的方法源碼部分如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"//即核心線程數=nThreads,最大線程數=nThreads\npublic static ExecutorService newFixedThreadPool(int nThreads) {\n        return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());\n}\n//核心線程數=1,最大線程數=1\npublic static ExecutorService newSingleThreadExecutor() {\n       return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, \n                                                                             new LinkedBlockingQueue()));\n}\n//核心線程數0,最大線程數=整形數的最大數\npublic static ExecutorService newCachedThreadPool() {\n    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue());\n}\n//\npublic static ScheduledExecutorService newSingleThreadScheduledExecutor() {\n        return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));\n}\n//ScheduledThreadPoolExecutor就是ThreadPoolExecutor子類\npublic ScheduledThreadPoolExecutor(int corePoolSize) {\n    //這裏的super就是ThreadPoolExecutor的構造方法\n    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());\n}\n//所以以上幾種創建線程池最終還是調用這個構造方法\npublic ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,                              \n                          TimeUnit unit, BlockingQueue workQueue) {        \n    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);\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":"可以看出,類似的其他方法一樣,在 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":"線程池的參數含義如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"corePoolSize 核心線程池大小;\nmaximumPoolSize 線程池最大容量大小;\nkeepAliveTime 線程池空閒時,線程存活的時間;\nTimeUnit 時間單位;\nThreadFactory 線程工廠;\nBlockingQueue任務隊列;\nRejectedExecutionHandler 線程拒絕策略;\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這也是一道非常高頻率的面試題,面試官一般會問你熟悉線程池嗎?你說熟悉,那就說一下jdk自帶的能創建多少中線程池,這裏你說個常見的上面幾種就基本ok了,但是你要是全部都知道的話那是更好,但是可能你把其他說出來了,你的面試官會懵逼的。所以面試官會繼續問說一下創建線程池有哪些參數,然後再針對上面的參數可以順帶着把線程池的原理也給面試官講一下。"}]}]},{"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":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"核心線程數"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"最大線程數"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"任務隊列"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"拒絕策略"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"任務隊列"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用到了三種阻塞同步隊列,LinkedBlockingQueue、SynchronousQueue、DelayedWorkQueue。都是BlockingQueue的實現類,AbstractQueue的子類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ce/ce896227bcd946808d84e9f4c37a20a4.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"LinkedBlockingQueue"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是一個由鏈表實現的有界隊列阻塞隊列,但大小默認值爲Integer.MAX_VALUE整形數的最大值,"}]},{"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":"所以我們在使用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","text":"newFixedThreadPool、newSingleThreadExecutor中都是使用這個隊列作爲任務隊列的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a096df149674eb02b3be5c9fd65ed1f5.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"SynchronousQueue"}]},{"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":"使用SynchronousQueue阻塞隊列一般要求maximumPoolSizes爲無界(Integer.MAX_VALUE),避免線程拒絕執行操作。"}]},{"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":"newCachedThreadPool中使用的就是SynchronousQueue作爲任務隊列。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"DelayedWorkQueue"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DelayedWorkQueue是基於堆結構的等待隊列。該隊列是定製的優先級隊列,只能用來存儲RunnableScheduledFutures任務。"}]},{"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":"堆是實現優先級隊列的最佳選擇,而該隊列正好是基於堆數據結構的實現,ScheduledThreadPoolExecutor的任務隊列就是使用了DelayedWorkQueue。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4e/4eac2a5fcb2394eaf5f969fea29195da.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"AbortPolicy:丟棄任務並拋出"}]},{"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":"RejectedExecutionException:異常"}]},{"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":"DiscardPolicy:丟棄任務,但是不拋出異常"}]},{"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":"DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新提交被拒絕的任務"}]},{"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":"CallerRunsPolicy:由調用線程(提交任務的線程)處理該任務"}]},{"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":"默認拒絕策略AbortPolicy"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"/**\n  * The default rejected execution handler\n  */\nprivate static final RejectedExecutionHandler defaultHandler = new AbortPolicy();\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"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":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"import java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Description:\n *\n * @author : tianweichang\n * @date : 2020/10/24\n * 公衆號:java後端技術全棧\n */\npublic class ThreadPoolDemo {\n    public static void main(String[] args) {\n        //指定4個線程的線程池\n        ExecutorService executorService = Executors.newFixedThreadPool(4);\n        for (int i = 0; i  System.out.println(\"i:\" + index + \" executorService\"));\n        }\n        executorService.shutdown();\n    }\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":"submit(Runnable task) 方法提交一個線程。"}]},{"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":"一般我們在IDEA要是裝了“阿里巴巴編碼規範插件”,當你使用上面這種方式創建線程池,然後就會提示你。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a0821b53543db9887255bd87440aa505.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a7/a7fa4dcbe2881a5ea4c53bb98db6bfbc.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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 的方式, 這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。"}]},{"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 各個方法的弊端:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"1)newFixedThreadPool和newSingleThreadExecutor:\n  主要問題是堆積的請求處理隊列可能會耗費非常大的內存,甚至OOM。\n2)newCachedThreadPool和newScheduledThreadPool:\n  主要問題是線程數最大數是Integer.MAX_VALUE,可能會創建數量非常多的線程,甚至OOM。\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":"另外這個插件還給咱們寫了兩個demo案例。"}]},{"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":"關於阿里巴巴編碼規範插件地址:https://github.com/alibaba/p3c"}]},{"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":"遵循阿里巴巴編碼規範的提示,示例如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public class ThreadPoolDemo {\n\n    public static void main(String[] args) {\n        //創建核心線程數=2,最大線程數=2的線程池\n        ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0L, \n                TimeUnit.MILLISECONDS, \n                new LinkedBlockingQueue<>(10), \n                Executors.defaultThreadFactory(), \n                new ThreadPoolExecutor.AbortPolicy());\n\n        for (int i = 0; i  System.out.println(\"i:\" + index +  \" executorService\"));\n        }\n        executorService.shutdown();\n    }\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":"或者這樣:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public class ThreadPoolDemo {\n\n    public static void main(String[] args) {\n\n        ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 2, 0L,\n                TimeUnit.MILLISECONDS,\n                new LinkedBlockingQueue<>(10),\n                Executors.defaultThreadFactory(),\n                new ThreadPoolExecutor.AbortPolicy());\n\n        for (int i = 0; i  System.out.println(\"i:\" + index + \" executorService\"));\n        }\n        pool.shutdown();\n    }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"execute() VS submit()方法"}]},{"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":"execute() 方法用於提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。"}]},{"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":"submit() 方法用於提交需要返回值的任務。"}]},{"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":"線程池會返回一個 future 類型的對象,通過這個 future 對象可以判斷任務是否執行成功,並且可以通過 future 的 get() 方法來獲取返回值,get() 方法會阻塞當前線程直到任務完成,而使用 get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間後立即返回,這時候有可能任務沒有執行完。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9d/9d014dbda63e4abb5802accf56b2f0ee.png","alt":"快速掌握併發編程---線程池的原理和實戰","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"1.https://blog.csdn.net/qq_43012792/"}]},{"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":"2.徐劉根的《java多線程編程核心技術》"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章