java中線程池最實(shí)用的創(chuàng)建與關(guān)閉指南
前言
在日常的開發(fā)工作當(dāng)中,線程池往往承載著一個(gè)應(yīng)用中最重要的業(yè)務(wù)邏輯,因此我們有必要更多地去關(guān)注線程池的執(zhí)行情況,包括異常的處理和分析等。
線程池創(chuàng)建
避免使用Executors創(chuàng)建線程池,主要是避免使用其中的默認(rèn)實(shí)現(xiàn),那么我們可以自己直接調(diào)用ThreadPoolExecutor的構(gòu)造函數(shù)來自己創(chuàng)建線程池。在創(chuàng)建的同時(shí),給BlockQueue指定容量就可以了。
private static ExecutorService executor = new ThreadPoolExecutor(10, 10,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue(10));
這種情況下,一旦提交的線程數(shù)超過當(dāng)前可用線程數(shù)時(shí),就會拋出java.util.concurrent.RejectedExecutionException,這是因?yàn)楫?dāng)前線程池使用的隊(duì)列是有邊界隊(duì)列,隊(duì)列已經(jīng)滿了便無法繼續(xù)處理新的請求。但是異常(Exception)總比發(fā)生錯誤(Error)要好。
除了自己定義ThreadPoolExecutor外。還有其他方法。這個(gè)時(shí)候第一時(shí)間就應(yīng)該想到開源類庫,如apache和guava等。
推薦使用guava提供的ThreadFactoryBuilder來創(chuàng)建線程池。
public class ExecutorsDemo {
private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
pool.execute(new SubThread());
}
}
}
只需要執(zhí)行shutdown就可以優(yōu)雅關(guān)閉
package com.zxd.concurrent;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
public static void main(String[] args) {
// TODO 如何正確優(yōu)雅簡單的關(guān)閉線程池 ,無須其他多余操 ;創(chuàng)建線程池我選擇用這種方法比較可靠
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
//接收處理數(shù)據(jù)的結(jié)果集合
List<Integer> resList = Lists.newArrayList();
//開啟的任務(wù) 我們設(shè)置的核心處理線程數(shù)5個(gè)所以 會有多出來的線程在隊(duì)列中等待
for (int i = 0; i < 30; i++) {
executor.execute(new Task(i, resList));
}
//1、關(guān)閉線程池 一定要在循環(huán)結(jié)束關(guān)閉
//2、這個(gè)關(guān)閉方法不會立即關(guān)閉所有在執(zhí)行的任務(wù)線程,
executor.shutdown();
//4.這里是檢查線程池是否所有任務(wù)都執(zhí)行完畢關(guān)閉
int j = 0;
while (true) {
//5.這里是等線程池徹底關(guān)閉以后做的判斷 保證所有線程池已經(jīng)全部關(guān)閉退出while循環(huán)
if (executor.isTerminated()) {
System.out.println("所有線程已經(jīng)運(yùn)行完畢:" + j);
break;
}
// 為避免一直循環(huán) 加個(gè)睡眠
try {
//如果執(zhí)行shutdown方法沒有關(guān)閉的線程池線程池會嘗試關(guān)閉
System.out.println("嘗試關(guān)閉線程次數(shù):" + j);
j++;
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//FIXME 3、下面的方法會立即關(guān)閉線程池,沒有執(zhí)行完的也不會在執(zhí)行了,如果有等待隊(duì)列的任務(wù)也不會繼續(xù)執(zhí)行
System.out.println("【完成的總線程數(shù)】:" + resList.size());
}
static class Task implements Runnable {
int name;
List<Integer> list;
public Task(int name, List<Integer> list) {
this.name = name;
this.list = list;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
int j = i * 10;
// 做業(yè)務(wù)處理
//System.out.println("task " + name + " is running");
}
list.add(name + 1);
System.out.println("task " + name + " is over");
}
}
}
輸出結(jié)果
task 0 is over
task 3 is over
task 1 is over
task 2 is over
task 7 is over
task 6 is over
task 5 is over
嘗試關(guān)閉線程次數(shù):0
task 10 is over
task 9 is over
task 4 is over
task 8 is over
task 14 is over
task 13 is over
task 12 is over
task 11 is over
task 18 is over
task 17 is over
task 16 is over
task 15 is over
task 22 is over
task 21 is over
task 20 is over
task 19 is over
task 26 is over
task 25 is over
task 24 is over
task 23 is over
task 29 is over
task 28 is over
task 27 is over
所有線程已經(jīng)運(yùn)行完畢:1
【完成的總線程數(shù)】:30
執(zhí)行shutdownNow關(guān)閉的測試
package com.zxd.concurrent;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
public static void main(String[] args) {
// TODO 如何正確優(yōu)雅簡單的關(guān)閉線程池 ,無須其他多余操 ;創(chuàng)建線程池我選擇用這種方法比較可靠
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
//接收處理數(shù)據(jù)的結(jié)果集合
List<Integer> resList = Lists.newArrayList();
//開啟的任務(wù) 我們設(shè)置的核心處理線程數(shù)5個(gè)所以 會有多出來的線程在隊(duì)列中等待
for (int i = 0; i < 200; i++) {
executor.execute(new Task(i, resList));
}
//1、關(guān)閉線程池 一定要在循環(huán)結(jié)束關(guān)閉
//2、這個(gè)關(guān)閉方法不會立即關(guān)閉所有在執(zhí)行的任務(wù)線程,
// executor.shutdown();
//FIXME 3、下面的方法會立即關(guān)閉線程池,沒有執(zhí)行完的也不會在執(zhí)行了,如果有等待隊(duì)列的任務(wù)也不會繼續(xù)執(zhí)行
List<Runnable> list = executor.shutdownNow();
System.out.println("c剩余的沒有執(zhí)行的任務(wù)【線程數(shù)】= " + list.size());
System.out.println("【完成的總線程數(shù)】:" + resList.size());
//4.這里是檢查線程池是否所有任務(wù)都執(zhí)行完畢關(guān)閉
int j = 0;
while (true) {
//5.這里是等線程池徹底關(guān)閉以后做的判斷 保證所有線程池已經(jīng)全部關(guān)閉退出while循環(huán)
if (executor.isTerminated()) {
System.out.println("所有線程已經(jīng)運(yùn)行完畢:" + j);
break;
}
// 為避免一直循環(huán) 加個(gè)睡眠
try {
//如果執(zhí)行shutdown方法沒有關(guān)閉的線程池線程池會嘗試關(guān)閉
System.out.println("嘗試關(guān)閉線程次數(shù):" + j);
j++;
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Task implements Runnable {
int name;
List<Integer> list;
public Task(int name, List<Integer> list) {
this.name = name;
this.list = list;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
int j = i * 10;
// 做業(yè)務(wù)處理
//System.out.println("task " + name + " is running");
}
list.add(name + 1);
System.out.println("task " + name + " is over");
}
}
}
輸出結(jié)果

總結(jié)
到此這篇關(guān)于java中線程池最實(shí)用的創(chuàng)建與關(guān)閉的文章就介紹到這了,更多相關(guān)java線程池創(chuàng)建與關(guān)閉內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis中注解與xml配置的對應(yīng)關(guān)系和對比分析
這篇文章主要介紹了mybatis中注解與xml配置的對應(yīng)關(guān)系和對比分析,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
SpringBoot多數(shù)據(jù)源切換實(shí)現(xiàn)代碼(Mybaitis)
實(shí)際工作中我們會遇到springboot項(xiàng)目初始化啟動時(shí)候,不能指定具體連接哪個(gè)數(shù)據(jù)源的時(shí)候,不同的接口連接不同的數(shù)據(jù)源或者前端頁面指定連接某個(gè)數(shù)據(jù)源等等情況,就會遇到動態(tài)數(shù)據(jù)源切換的問題,需要的朋友可以參考下2022-04-04
java實(shí)現(xiàn)ssh登錄linux并執(zhí)行命令的三種實(shí)現(xiàn)方式
文章介紹了三種在Java中實(shí)現(xiàn)SSH登錄Linux并執(zhí)行命令的方法,包括使用ganymed-ssh2、jsch和sshd-core,由于ganymed-ssh2和jsch的最新版本較舊,可能無法與較新的Linux系統(tǒng)兼容,而sshd-core一直在更新,推薦使用2024-11-11
Mybatis查找返回Map,List集合類型的數(shù)據(jù)方式
這篇文章主要介紹了Mybatis查找返回Map,List集合類型的數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
關(guān)于SpingMVC的<context:component-scan>包掃描踩坑記錄
這篇文章主要介紹了關(guān)于SpingMVC的<context:component-scan>包掃描踩坑記錄,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
java鏈表數(shù)據(jù)結(jié)構(gòu)LinkedList插入刪除元素時(shí)間復(fù)雜度面試精講
這篇文章主要為大家介紹了java LinkedList插入和刪除元素的時(shí)間復(fù)雜度面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10

