日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

为什么创建线程池一定要用ThreadPoolExecutor?

發布時間:2025/3/11 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 为什么创建线程池一定要用ThreadPoolExecutor? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者 | 磊哥

來源 | Java面試真題解析(ID:aimianshi666)

轉載請聯系授權(微信ID:GG_Stone)

在 Java 語言中,并發編程都是依靠線程池完成的,而線程池的創建方式又有很多,但從大的分類來說,線程池的創建總共分為兩大類:手動方式使用 ThreadPoolExecutor 創建線程池和使用 Executors 執行器自動創建線程池。那究竟要使用哪種方式來創建線程池呢?我們今天就來詳細的聊一聊。

先說結論

在 Java 語言中,一定要使用 ThreadPoolExecutor 手動的方式來創建線程池,因為這種方式可以通過參數來控制最大任務數和拒絕策略,讓線程池的執行更加透明和可控,并且可以規避資源耗盡的風險。

OOM風險演示

假如我們使用了 Executors 執行器自動創建線程池的方式來創建線程池,那么就會存現線程溢出的風險,以 CachedThreadPool 為例我們來演示一下:

import?java.util.ArrayList; import?java.util.List; import?java.util.concurrent.ExecutorService; import?java.util.concurrent.Executors;public?class?ThreadPoolExecutorExample?{static?class?OOMClass?{//?創建?1MB?大小的變量(1M?=?1024KB?=?1024*1024Byte)private?byte[]?data_byte?=?new?byte[1?*?1024?*?1024];}public?static?void?main(String[]?args)?throws?InterruptedException?{//?使用執行器自動創建線程池ExecutorService?threadPool?=?Executors.newCachedThreadPool();List<Object>?list?=?new?ArrayList<>();//?添加任務for?(int?i?=?0;?i?<?10;?i++)?{int?finalI?=?i;threadPool.execute(new?Runnable()?{@Overridepublic?void?run()?{//?定時添加try?{Thread.sleep(finalI?*?200);}?catch?(InterruptedException?e)?{e.printStackTrace();}//?將?1M?對象添加到集合OOMClass?oomClass?=?new?OOMClass();list.add(oomClass);System.out.println("執行任務:"?+?finalI);}});}} }

第 2 步將 Idea 中 JVM 最大運行內存設置為 10M(設置此值主要是為了方便演示),如下圖所示:以上程序的執行結果如下圖所示:從上述結果可以看出,當線程執行了 7 次之后就開始出現 OutOfMemoryError 內存溢出的異常了。

內存溢出原因分析

想要了解內存溢出的原因,我們需要查看 CachedThreadPool 實現的細節,它的源碼如下圖所示:構造函數的第 2 個參數被設置成了 Integer.MAX_VALUE,這個參數的含義是最大線程數,所以由于 CachedThreadPool 并不限制線程的數量,當任務數量特別多的時候,就會創建非常多的線程。而上面的 OOM 示例,每個線程至少要消耗 1M 大小的內存,加上 JDK 系統類的加載也要占用一部分的內存,所以當總的運行內存大于 10M 的時候,就出現內存溢出的問題了。

使用ThreadPoolExecutor來改進

接下來我們使用 ThreadPoolExecutor 來改進一下 OOM 的問題,我們使用 ThreadPoolExecutor 手動創建線程池的方式,創建一個最大線程數為 2,最多可存儲 2 個任務的線程池,并且設置線程池的拒絕策略為忽略新任務,這樣就能保證線程池的運行內存大小不會超過 10M 了,實現代碼如下:

import?java.util.ArrayList; import?java.util.List; import?java.util.concurrent.*;/***?ThreadPoolExecutor?演示示例*/ public?class?ThreadPoolExecutorExample?{static?class?OOMClass?{//?創建?1MB?大小的變量(1M?=?1024KB?=?1024*1024Byte)private?byte[]?data_byte?=?new?byte[1?*?1024?*?1024];}public?static?void?main(String[]?args)?throws?InterruptedException?{//?手動創建線程池,最大線程數?2,最多存儲?2?個任務,其他任務會被忽略ThreadPoolExecutor?threadPool?=?new?ThreadPoolExecutor(2,?2,0L,?TimeUnit.SECONDS,?new?LinkedBlockingQueue<>(2),new?ThreadPoolExecutor.DiscardPolicy());?//?拒絕策略:忽略任務List<Object>?list?=?new?ArrayList<>();//?添加任務for?(int?i?=?0;?i?<?10;?i++)?{int?finalI?=?i;threadPool.execute(new?Runnable()?{@Overridepublic?void?run()?{//?定時添加try?{Thread.sleep(finalI?*?200);}?catch?(InterruptedException?e)?{e.printStackTrace();}//?將?1m?對象添加到集合OOMClass?oomClass?=?new?OOMClass();list.add(oomClass);System.out.println("執行任務:"?+?finalI);}});}//?關閉線程池threadPool.shutdown();//?檢測線程池的任務執行完while?(!threadPool.awaitTermination(3,?TimeUnit.SECONDS))?{System.out.println("線程池中還有任務在處理");}} }

以上程序的執行結果如下圖所示:從上述結果可以看出,線程池從開始執行到執行結束都沒有出現 OOM 的異常,這就是手動創建線程池的優勢。

其他創建線程池的問題

除了 CachedThreadPool 線程池之外,其他使用 Executors 自動創建線程池的方式,也存在著其他一些問題,比如 FixedThreadPool 它的實現源碼如下:而默認情況下任務隊列 LinkedBlockingQueue 的存儲容量是 Integer.MAX_VALUE,也是趨向于無限大,如下圖所示:這樣就也會造成,因為線程池的任務過多而導致的內存溢出問題。其他幾個使用 Executors 自動創建線程池的方式也存在此問題,這里就不一一演示了。

總結

線程池的創建方式總共分為兩大類:手動使用 ThreadPoolExecutor 創建線程池和自動使用 Executors 執行器創建線程池的方式。其中使用 Executors 自動創建線程的方式,因為線程個數或者任務個數不可控,可能會導致內存溢出的風險,所以在創建線程池時,建議使用 ThreadPoolExecutor 的方式來創建。

是非審之于己,毀譽聽之于人,得失安之于數。

公眾號:Java面試真題解析

面試合集:https://gitee.com/mydb/interview

往期推薦

面試突擊31:什么是守護線程?它和用戶線程有什么區別?


面試突擊30:線程池是如何執行的?拒絕策略有哪些?


面試突擊29:說一下線程池7個參數的含義?


總結

以上是生活随笔為你收集整理的为什么创建线程池一定要用ThreadPoolExecutor?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。