自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試突擊:為什么創(chuàng)建線程池一定要用ThreadPoolExecutor?

開發(fā) 前端
線程池的創(chuàng)建方式總共分為兩大類:手動(dòng)使用 ThreadPoolExecutor 創(chuàng)建線程池和自動(dòng)使用 Executors 執(zhí)行器創(chuàng)建線程池的方式。

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

先說結(jié)論

在 Java 語言中,一定要使用 ThreadPoolExecutor 手動(dòng)的方式來創(chuàng)建線程池,因?yàn)檫@種方式可以通過參數(shù)來控制最大任務(wù)數(shù)和拒絕策略,讓線程池的執(zhí)行更加透明和可控,并且可以規(guī)避資源耗盡的風(fēng)險(xiǎn)。

OOM風(fēng)險(xiǎn)演示

假如我們使用了 Executors 執(zhí)行器自動(dòng)創(chuàng)建線程池的方式來創(chuàng)建線程池,那么就會(huì)存現(xiàn)線程溢出的風(fēng)險(xiǎn),以 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 {
// 創(chuàng)建 1MB 大小的變量(1M = 1024KB = 1024*1024Byte)
private byte[] data_byte = new byte[1 * 1024 * 1024];
}
public static void main(String[] args) throws InterruptedException {
// 使用執(zhí)行器自動(dòng)創(chuàng)建線程池
ExecutorService threadPool = Executors.newCachedThreadPool();
List<Object> list = new ArrayList<>();
// 添加任務(wù)
for (int i = 0; i < 10; i++) {
int finalI = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
// 定時(shí)添加
try {
Thread.sleep(finalI * 200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//1M 對象添加到集合
OOMClass oomClass = new OOMClass();
list.add(oomClass);
System.out.println("執(zhí)行任務(wù):" + finalI);
}
});
}
}
}

第 2 步將 Idea 中 JVM 最大運(yùn)行內(nèi)存設(shè)置為 10M(設(shè)置此值主要是為了方便演示),如下圖所示:

以上程序的執(zhí)行結(jié)果如下圖所示:

從上述結(jié)果可以看出,當(dāng)線程執(zhí)行了 7 次之后就開始出現(xiàn) OutOfMemoryError 內(nèi)存溢出的異常了。

內(nèi)存溢出原因分析

想要了解內(nèi)存溢出的原因,我們需要查看 CachedThreadPool 實(shí)現(xiàn)的細(xì)節(jié),它的源碼如下圖所示:

構(gòu)造函數(shù)的第 2 個(gè)參數(shù)被設(shè)置成了 Integer.MAX_VALUE,這個(gè)參數(shù)的含義是最大線程數(shù),所以由于 CachedThreadPool 并不限制線程的數(shù)量,當(dāng)任務(wù)數(shù)量特別多的時(shí)候,就會(huì)創(chuàng)建非常多的線程。而上面的 OOM 示例,每個(gè)線程至少要消耗 1M 大小的內(nèi)存,加上 JDK 系統(tǒng)類的加載也要占用一部分的內(nèi)存,所以當(dāng)總的運(yùn)行內(nèi)存大于 10M 的時(shí)候,就出現(xiàn)內(nèi)存溢出的問題了。

使用ThreadPoolExecutor來改進(jìn)

接下來我們使用 ThreadPoolExecutor 來改進(jìn)一下 OOM 的問題,我們使用 ThreadPoolExecutor 手動(dòng)創(chuàng)建線程池的方式,創(chuàng)建一個(gè)最大線程數(shù)為 2,最多可存儲(chǔ) 2 個(gè)任務(wù)的線程池,并且設(shè)置線程池的拒絕策略為忽略新任務(wù),這樣就能保證線程池的運(yùn)行內(nèi)存大小不會(huì)超過 10M 了,實(shí)現(xiàn)代碼如下:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
* ThreadPoolExecutor 演示示例
*/
public class ThreadPoolExecutorExample {
static class OOMClass {
// 創(chuàng)建 1MB 大小的變量(1M = 1024KB = 1024*1024Byte)
private byte[] data_byte = new byte[1 * 1024 * 1024];
}

public static void main(String[] args) throws InterruptedException {
// 手動(dòng)創(chuàng)建線程池,最大線程數(shù) 2,最多存儲(chǔ) 2 個(gè)任務(wù),其他任務(wù)會(huì)被忽略
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 2,
0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2),
new ThreadPoolExecutor.DiscardPolicy()); // 拒絕策略:忽略任務(wù)
List<Object> list = new ArrayList<>();
// 添加任務(wù)
for (int i = 0; i < 10; i++) {
int finalI = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
// 定時(shí)添加
try {
Thread.sleep(finalI * 200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//1m 對象添加到集合
OOMClass oomClass = new OOMClass();
list.add(oomClass);
System.out.println("執(zhí)行任務(wù):" + finalI);
}
});
}
// 關(guān)閉線程池
threadPool.shutdown();
// 檢測線程池的任務(wù)執(zhí)行完
while (!threadPool.awaitTermination(3, TimeUnit.SECONDS)) {
System.out.println("線程池中還有任務(wù)在處理");
}
}
}

以上程序的執(zhí)行結(jié)果如下圖所示:

從上述結(jié)果可以看出,線程池從開始執(zhí)行到執(zhí)行結(jié)束都沒有出現(xiàn) OOM 的異常,這就是手動(dòng)創(chuàng)建線程池的優(yōu)勢。

其他創(chuàng)建線程池的問題

除了 CachedThreadPool 線程池之外,其他使用 Executors 自動(dòng)創(chuàng)建線程池的方式,也存在著其他一些問題,比如 FixedThreadPool 它的實(shí)現(xiàn)源碼如下:

而默認(rèn)情況下任務(wù)隊(duì)列 LinkedBlockingQueue 的存儲(chǔ)容量是 Integer.MAX_VALUE,也是趨向于無限大,如下圖所示:

這樣就也會(huì)造成,因?yàn)榫€程池的任務(wù)過多而導(dǎo)致的內(nèi)存溢出問題。其他幾個(gè)使用 Executors 自動(dòng)創(chuàng)建線程池的方式也存在此問題,這里就不一一演示了。

總結(jié)線程池的創(chuàng)建方式總共分為兩大類:手動(dòng)使用 ThreadPoolExecutor 創(chuàng)建線程池和自動(dòng)使用 Executors 執(zhí)行器創(chuàng)建線程池的方式。其中使用 Executors 自動(dòng)創(chuàng)建線程的方式,因?yàn)榫€程個(gè)數(shù)或者任務(wù)個(gè)數(shù)不可控,可能會(huì)導(dǎo)致內(nèi)存溢出的風(fēng)險(xiǎn),所以在創(chuàng)建線程池時(shí),建議使用 ThreadPoolExecutor 的方式來創(chuàng)建。

責(zé)任編輯:武曉燕 來源: Java面試真題解析
相關(guān)推薦

2022-05-26 09:24:09

volatile懶漢模式

2019-01-29 11:02:30

消息中間件Java互聯(lián)網(wǎng)

2022-03-02 07:36:37

池化技術(shù)Java線程池

2018-08-24 09:02:26

2019-02-14 09:35:15

面試MQ中間件

2022-07-13 07:06:47

HTTPSHTTP協(xié)議

2021-03-05 11:02:14

iOS 14.5蘋果更新

2020-12-23 13:29:15

微服務(wù)架構(gòu)面試官

2022-01-24 07:01:20

安全多線程版本

2019-09-19 15:12:27

Spring策略框架

2022-03-07 07:33:16

線程池Java語言

2021-12-03 06:59:23

HashCodeEquals面試

2021-12-13 09:10:48

equalshashCodeJava

2011-05-10 15:51:34

SEO

2022-04-24 09:54:24

ProxyReflect前端

2022-07-14 15:08:23

AI模型

2022-01-10 13:06:13

微服務(wù)API網(wǎng)關(guān)

2022-07-27 07:36:01

TCP可靠性

2022-02-18 12:24:39

PythonNumpy Arra大數(shù)據(jù)

2024-02-22 14:22:17

數(shù)字化轉(zhuǎn)型企業(yè)架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號