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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

面试突击32:为什么创建线程池一定要用ThreadPoolExecutor?

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

Python微信訂餐小程序課程視頻

https://edu.csdn.net/course/detail/36074

Python實戰量化交易理財系統

https://edu.csdn.net/course/detail/35475
在 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 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(設置此值主要是為了方便演示),如下圖所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/efdd083c363a9b9c41ee5363b857c309.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=683&id=ueb99cd9c&margin=[object Object]&name=image.png&originHeight=1365&originWidth=2150&originalType=binary&ratio=1&rotation=0&showTitle=false&size=198199&status=done&style=none&taskId=ueeb2e01e-0a7f-4b57-a4fb-c19fd8e47eb&title=&width=1075)
以上程序的執行結果如下圖所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/241d582f9175a1fe751befb3e7be71fa.png#clientId=ud56411a7-ab24-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=573&id=ud7b6df8e&margin=[object Object]&name=image.png&originHeight=1145&originWidth=2924&originalType=binary&ratio=1&rotation=0&showTitle=false&size=294569&status=done&style=none&taskId=ub5f0a310-0317-4cbc-86e7-daec5dd030c&title=&width=1462)
從上述結果可以看出,當線程執行了 7 次之后就開始出現 OutOfMemoryError 內存溢出的異常了。

內存溢出原因分析

想要了解內存溢出的原因,我們需要查看 CachedThreadPool 實現的細節,它的源碼如下圖所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/f5d6b1a0f01cb3d8cc3c5affb0782626.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=199&id=u5c9c7355&margin=[object Object]&name=image.png&originHeight=397&originWidth=1806&originalType=binary&ratio=1&rotation=0&showTitle=false&size=83232&status=done&style=none&taskId=uc90443de-70e3-4873-a054-3380de696d6&title=&width=903)
構造函數的第 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 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("線程池中還有任務在處理");}} }

以上程序的執行結果如下圖所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/4883cd229e15f09ea3dcc25d23a4e33a.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=260&id=ude5706f1&margin=[object Object]&name=image.png&originHeight=520&originWidth=1996&originalType=binary&ratio=1&rotation=0&showTitle=false&size=70067&status=done&style=none&taskId=u49f0d4bc-b1f0-49a8-8b4e-706075c0c4f&title=&width=998)
從上述結果可以看出,線程池從開始執行到執行結束都沒有出現 OOM 的異常,這就是手動創建線程池的優勢。

其他創建線程池的問題

除了 CachedThreadPool 線程池之外,其他使用 Executors 自動創建線程池的方式,也存在著其他一些問題,比如 FixedThreadPool 它的實現源碼如下:
![image.png](https://img-blog.csdnimg.cn/img_convert/741be157ff698dd9564d53a566197cae.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=164&id=u0796b9b1&margin=[object Object]&name=image.png&originHeight=327&originWidth=1830&originalType=binary&ratio=1&rotation=0&showTitle=false&size=70565&status=done&style=none&taskId=ubf50d492-479e-4d94-9fb4-b3cb470e0ad&title=&width=915)
而默認情況下任務隊列 LinkedBlockingQueue 的存儲容量是 Integer.MAX_VALUE,也是趨向于無限大,如下圖所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/8ce7ad1e0242f0852bdb65dd6386e184.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=236&id=u43e680d2&margin=[object Object]&name=image.png&originHeight=472&originWidth=1911&originalType=binary&ratio=1&rotation=0&showTitle=false&size=78162&status=done&style=none&taskId=u0d4d184f-3189-4085-860a-5b0f559d3d8&title=&width=955.5)
這樣就也會造成,因為線程池的任務過多而導致的內存溢出問題。其他幾個使用 Executors 自動創建線程池的方式也存在此問題,這里就不一一演示了。

總結

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

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

公眾號:Java面試真題解析

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

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

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

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