南充做網(wǎng)站公司哪家好營銷型網(wǎng)站推廣
線程池簡述
為什么需要一個(gè)線程池工具類?
答:整個(gè)項(xiàng)目,用到線程執(zhí)行任務(wù)的地方很多,不可能哪里用到就在那里直接new一個(gè)線程執(zhí)行,這樣資源得不到重復(fù)利用,一旦線程過多就會(huì)導(dǎo)致內(nèi)存不足。
線程池的好處是什么?
答:使用線程池執(zhí)行線程任務(wù),當(dāng)一個(gè)線程執(zhí)行完成一個(gè)任務(wù)之后,線程資源回到線程池,資源得到重復(fù)利用。
線程池為什么使用自定義方式?
阿里文檔推薦使用自定義線程池,因?yàn)閖ava自帶線程池都會(huì)有可能造成內(nèi)存不足的問題。自定義線程池,根據(jù)服務(wù)器配置定制線程池核心線程、最大線程等,是最好的方式。
二、工具類代碼和測試代碼
導(dǎo)包
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version></dependency>
工具類?
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*;/*** 自定義線程創(chuàng)建工具類,創(chuàng)建線程池后不需要關(guān)閉** @author liangxn*/
public class ThreadPoolUtils {private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolUtils.class);private static ThreadPoolExecutor threadPool = null;private static final String POOL_NAME = "myPool";// 等待隊(duì)列長度private static final int BLOCKING_QUEUE_LENGTH = 20000;// 閑置線程存活時(shí)間private static final int KEEP_ALIVE_TIME = 60 * 1000;private ThreadPoolUtils() {throw new IllegalStateException("utility class");}/*** 無返回值直接執(zhí)行** @param runnable 需要運(yùn)行的任務(wù)*/public static void execute(Runnable runnable) {getThreadPool().execute(runnable);}/*** 有返回值執(zhí)行* 主線程中使用Future.get()獲取返回值時(shí),會(huì)阻塞主線程,直到任務(wù)執(zhí)行完畢** @param callable 需要運(yùn)行的任務(wù)*/public static <T> Future<T> submit(Callable<T> callable) {return getThreadPool().submit(callable);}public static synchronized ThreadPoolExecutor getThreadPool() {if (threadPool == null) {// 獲取處理器數(shù)量int cpuNum = Runtime.getRuntime().availableProcessors();// 根據(jù)cpu數(shù)量,計(jì)算出合理的線程并發(fā)數(shù)int maximumPoolSize = cpuNum * 2 + 1;// 核心線程數(shù)、最大線程數(shù)、閑置線程存活時(shí)間、時(shí)間單位、線程隊(duì)列、線程工廠、當(dāng)前線程數(shù)已經(jīng)超過最大線程數(shù)時(shí)的異常處理策略threadPool = new ThreadPoolExecutor(maximumPoolSize - 1,maximumPoolSize,KEEP_ALIVE_TIME,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(BLOCKING_QUEUE_LENGTH),new ThreadFactoryBuilder().setNameFormat(POOL_NAME + "-%d").build(),new ThreadPoolExecutor.AbortPolicy() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor e) {LOGGER.warn("線程爆炸了,當(dāng)前運(yùn)行線程總數(shù):{},活動(dòng)線程數(shù):{}。等待隊(duì)列已滿,等待運(yùn)行任務(wù)數(shù):{}",e.getPoolSize(),e.getActiveCount(),e.getQueue().size());}});}return threadPool;}
}
?線程池的七個(gè)參數(shù)講解
- corePoolSize:線程池核心大小。線程池中會(huì)維護(hù)最小的線程線程數(shù)量,即使這些線程處于空閑狀態(tài),他們也不會(huì)銷毀,除非設(shè)置了allowCoreThreadTimeOut。
- maximumPoolSize:線程池最大線程數(shù)量。一個(gè)任務(wù)被提交到線程池以后,首先會(huì)找有沒有空閑存活的線程,如果有則直接將任務(wù)提交給空閑線程,如果沒有,就會(huì)緩存到工作隊(duì)列中。如果工作隊(duì)列滿了,才會(huì)新建一個(gè)線程,然后從工作隊(duì)列的頭部去除一個(gè)任務(wù)交由新線程來處理,而將剛提交的任務(wù)放入工作隊(duì)列的尾部。線程池不會(huì)無限制的去創(chuàng)建新線程,它會(huì)有一個(gè)最大線程數(shù)量限制,這個(gè)數(shù)量即由maximunPoolSize指定。
- keepAliveTime:空閑線程存活時(shí)間。一個(gè)線程如果處于空閑狀態(tài),且當(dāng)前的線程數(shù)量大于corePoolSize,那么在指定時(shí)間后,這個(gè)空閑線程就會(huì)被銷毀。
- unit:空閑線程存活時(shí)間單位。keepAliveTime的計(jì)量單位。
- workQueue:工作隊(duì)列。新任務(wù)被提交以后,會(huì)進(jìn)入到此工作隊(duì)列中,任務(wù)調(diào)度時(shí)再從隊(duì)列中取出任務(wù)。
- threadFactory:線程工廠。創(chuàng)建一個(gè)新線程使用的工廠,可以用來設(shè)定線程名
- hander:拒絕策略。當(dāng)工作隊(duì)列中的任務(wù)已達(dá)到最大限制,并且線程池中的線程數(shù)量已達(dá)到最大限制,這時(shí)如果有新任務(wù)提交進(jìn)來,該如何處理呢。這里的拒絕策略,就是解決這個(gè)問題。
四種工作隊(duì)列
- ArrayBlockingQueue:基于數(shù)組的有界阻塞隊(duì)列,按FIFO排序。新任務(wù)進(jìn)來后,會(huì)放到該隊(duì)列的隊(duì)尾,有界的數(shù)組可以防止資源耗盡問題。當(dāng)線程池中的線程數(shù)量達(dá)到corePoolSize后,再有新任務(wù)進(jìn)來,則會(huì)將任務(wù)放入該隊(duì)列的隊(duì)尾,等待被調(diào)度。如果隊(duì)列已經(jīng)滿了,則創(chuàng)建一個(gè)新的線程,如果線程數(shù)量已經(jīng)達(dá)到了maxPoolSize,則會(huì)執(zhí)行拒絕策略。
- LinkedBlockingQuene:基于鏈表的無界阻塞隊(duì)列(其實(shí)最大容量為Integer.MAX)。按FIFO排序。由于該隊(duì)列的近似無界性,當(dāng)線程池中線程數(shù)量達(dá)到了corePoolSize之后,再有新任務(wù)進(jìn)來,會(huì)一直存在該隊(duì)列,而不會(huì)去創(chuàng)建新的線程,知道m(xù)axPoolSize,因此使用該工作隊(duì)列時(shí),參數(shù)ma'xPoolSize其實(shí)是不起作用的。
- SynchronousQuene:一個(gè)不緩存任務(wù)的阻塞隊(duì)列。生產(chǎn)者放入一個(gè)任務(wù),必須等到消費(fèi)者取出這個(gè)任務(wù),也就是說,新任務(wù)進(jìn)來時(shí),不會(huì)緩存,而是直接被調(diào)度執(zhí)行該任務(wù),如果沒有可用線程,則創(chuàng)建新線程,如果線程數(shù)量達(dá)到maxPoolSize,則執(zhí)行拒絕策略。
- PriorityBlockingQueue:具有優(yōu)先級的無界阻塞隊(duì)列,直到資源耗盡。默認(rèn)情況下,元素采用自然排序升序排列。也可以自定義實(shí)現(xiàn)類compareTo()方法來指定元素排序規(guī)則,或者初始化PriorityBlockingQueue時(shí),指定構(gòu)造參數(shù)Comparator來對元素進(jìn)行排序。但需要注意的是,不能保證同優(yōu)先級元素的順序。
四種拒絕策略
ThreadPoolExecutor.AbortPolicy | 直接拋出異常(默認(rèn)拒絕策略) |
?ThreadPoolExecutor.DiscardPolicy | 丟棄當(dāng)前被拒絕的任務(wù),而不拋出異常 |
ThreadPoolExecutor.DiscardOldestPolicy | 將工作任務(wù)中最老的任務(wù)丟棄,然后重新嘗試接納被拒絕的任務(wù) |
ThreadPoolExecutor.CallerRunsPolicy | 在客戶端中執(zhí)行被拒絕的任務(wù) |
例子1
Future<String> future = ThreadPoolUtils.submit(() -> {return "我有返回值哦";});try {logger.info(future.get());} catch (InterruptedException | ExecutionException e) {logger.error("任務(wù)超過指定時(shí)間未返回值,線程超時(shí)退出");}?// 控制臺打印日志:21:04:19.428 [main] INFO - 我有返回值哦
例子2
Future<String> future = ThreadPoolUtils.submit(() -> {return "我有返回值哦";});try {logger.info(future.get());} catch (InterruptedException | ExecutionException e) {logger.error("任務(wù)超過指定時(shí)間未返回值,線程超時(shí)退出");}// 控制臺打印日志:21:04:19.428 [main] INFO - 我有返回值哦
例子3
int loop = 40;
for (int i = 0; i < loop; i++) {logger.info("任務(wù){(diào)}", i);ThreadPoolUtils.execute(() -> {logger.info("干活好累");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}logger.info("終于干完了");});}logger.info("我在這兒等著你回來等你回來");// 控制臺打印:21:08:08.494 [main] INFO - 任務(wù)0............21:08:08.540 [main] INFO - 任務(wù)521:08:08.541 [main] INFO - 任務(wù)621:08:08.540 [myPool-4] INFO - 干活好累21:08:08.540 [myPool-1] INFO - 干活好累21:08:08.540 [myPool-3] INFO - 干活好累............21:08:08.543 [main] INFO - 任務(wù)2121:08:08.548 [main] INFO - 任務(wù)2221:08:08.548 [main] INFO - 任務(wù)2321:08:08.548 [myPool-21] INFO - 干活好累21:08:08.549 [main] INFO - 任務(wù)2421:08:08.549 [myPool-22] INFO - 干活好累21:08:08.549 [main] INFO - 任務(wù)2521:08:08.549 [myPool-23] INFO - 干活好累21:08:08.549 [main] INFO - 任務(wù)26............21:08:08.551 [myPool-1] INFO - 干活好累21:08:08.551 [myPool-6] INFO - 終于干完了21:08:08.551 [myPool-7] INFO - 終于干完了21:08:08.551 [myPool-5] INFO - 干活好累21:08:08.551 [main] INFO - 任務(wù)3521:08:08.551 [main] INFO - 任務(wù)3621:08:08.551 [main] INFO - 任務(wù)3721:08:08.551 [main] INFO - 任務(wù)3821:08:08.551 [main] INFO - 任務(wù)3921:08:08.551 [main] INFO - 我在這兒等著你回來等你回來21:08:08.551 [myPool-2] INFO - 干活好累21:08:08.551 [myPool-3] INFO - 干活好累21:08:08.551 [myPool-8] INFO - 干活好累21:08:08.551 [myPool-6] INFO - 干活好累21:08:08.551 [myPool-7] INFO - 干活好累21:08:08.552 [myPool-13] INFO - 終于干完了21:08:08.552 [myPool-12] INFO - 終于干完了............21:08:08.561 [myPool-7] INFO - 終于干完了21:08:08.561 [myPool-3] INFO - 終于干完了
例子4
// 測試10個(gè)線程使用工具類
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {executorService.submit(new Runnable() {@Overridepublic void run() {final String name = Thread.currentThread().getName();ThreadPoolUtils.execute(() -> {logger.info("[{}],干活好累", name);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}logger.info("[{}],終于干完了", name);});}});}logger.info("不用等他,我們先干");// 控制臺打印:21:11:49.946 [main] INFO - 不用等他,我們先干21:11:49.991 [myPool-4] INFO - [pool-2-thread-7],干活好累21:11:49.991 [myPool-3] INFO - [pool-2-thread-2],干活好累21:11:49.991 [myPool-5] INFO - [pool-2-thread-5],干活好累21:11:49.991 [myPool-8] INFO - [pool-2-thread-6],干活好累21:11:49.991 [myPool-1] INFO - [pool-2-thread-3],干活好累21:11:49.991 [myPool-2] INFO - [pool-2-thread-9],干活好累21:11:49.991 [myPool-9] INFO - [pool-2-thread-10],干活好累21:11:49.991 [myPool-7] INFO - [pool-2-thread-1],干活好累21:11:49.991 [myPool-6] INFO - [pool-2-thread-4],干活好累21:11:49.991 [myPool-0] INFO - [pool-2-thread-8],干活好累21:11:50.091 [myPool-7] INFO - [pool-2-thread-1],終于干完了21:11:50.091 [myPool-4] INFO - [pool-2-thread-7],終于干完了21:11:50.091 [myPool-5] INFO - [pool-2-thread-5],終于干完了21:11:50.091 [myPool-2] INFO - [pool-2-thread-9],終于干完了21:11:50.091 [myPool-0] INFO - [pool-2-thread-8],終于干完了21:11:50.091 [myPool-1] INFO - [pool-2-thread-3],終于干完了21:11:50.091 [myPool-8] INFO - [pool-2-thread-6],終于干完了21:11:50.091 [myPool-6] INFO - [pool-2-thread-4],終于干完了21:11:50.091 [myPool-3] INFO - [pool-2-thread-2],終于干完了21:11:50.091 [myPool-9] INFO - [pool-2-thread-10],終于干完了
例子5
int loop = 2000;
for (int i = 0; i < loop; i++) {ThreadPoolUtils.execute(() -> {logger.info("干活好累");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}logger.info("終于干完了");});}logger.info("不用等他,我們先干");?// 控制臺打印:............21:13:25.083 [myPool-19] INFO - 干活好累21:13:25.083 [myPool-8] INFO - 干活好累21:13:25.083 [myPool-30] INFO - 干活好累21:13:25.085 [main] WARN - 線程爆炸了,當(dāng)前運(yùn)行線程總數(shù):33,活動(dòng)線程數(shù):33。等待隊(duì)列已滿,等待運(yùn)行任務(wù)數(shù):100021:13:25.085 [main] WARN - 線程爆炸了,當(dāng)前運(yùn)行線程總數(shù):33,活動(dòng)線程數(shù):33。等待隊(duì)列已滿,等待運(yùn)行任務(wù)數(shù):100021:13:25.085 [main] WARN - 線程爆炸了,當(dāng)前運(yùn)行線程總數(shù):33,活動(dòng)線程數(shù):33。等待隊(duì)列已滿,等待運(yùn)行任務(wù)數(shù):100021:13:25.085 [main] WARN - 線程爆炸了,當(dāng)前運(yùn)行線程總數(shù):33,活動(dòng)線程數(shù):33。等待隊(duì)列已滿,等待運(yùn)行任務(wù)數(shù):100021:13:25.106 [myPool-7] INFO - 干活好累21:13:25.106 [myPool-11] INFO - 干活好累21:13:25.106 [main] WARN - 線程爆炸了,當(dāng)前運(yùn)行線程總數(shù):33,活動(dòng)線程數(shù):33。等待隊(duì)列已滿,等待運(yùn)行任務(wù)數(shù):100021:13:25.106 [myPool-6] INFO - 干活好累21:13:25.106 [myPool-4] INFO - 干活好累21:13:25.106 [main] WARN - 線程爆炸了,當(dāng)前運(yùn)行線程總數(shù):33,活動(dòng)線程數(shù):33。等待隊(duì)列已滿,等待運(yùn)行任務(wù)數(shù):100021:13:25.106 [main] WARN - 線程爆炸了,當(dāng)前運(yùn)行線程總數(shù):33,活動(dòng)線程數(shù):33。等待隊(duì)列已滿,等待運(yùn)行任務(wù)數(shù):1000............