「Java工具類」java線程池工具類,自定義線程池工具類
介紹語
本博客主要是Java常用關鍵技術點,通用工具類的分享;以及springboot+springcloud+Mybatisplus+druid+mysql+redis+swagger+maven+docker等集成框架的技術分享;datax、kafka、flink等大數據處理框架的技術分享。文章會不斷更新,歡迎碼友關注點贊收藏轉發!
關注多的話,后面會錄制一些視頻教程,圖文和視頻結合,比如:圖書介紹網站系統、搶購系統、大數據中臺系統等。技術才是程序猿的最愛,碼友們沖啊
正文:
java核心之一就是線程,而用線程池執行線程是好的方式。下面是我封裝的一個線程池工具類,簡單實用。歡迎收藏備用!
-
為什么需要一個線程池工具類?
答:整個項目,用到線程執行任務的地方很多,不可能哪里用到就在那里直接new一個線程執行,這樣資源得不到重復利用,一旦線程過多就會導致內存不足。
-
線程池的好處是什么?
使用線程池執行線程任務,當一個線程執行完成一個任務之后,線程資源回到線程池,資源得到重復利用。
-
線程池為什么使用自定義方式?
阿里文檔推薦使用自定義線程池,因為java自帶線程池都會有可能造成內存不足的問題。自定義線程池,根據服務器配置定制線程池核心線程、最大線程等,是最好的方式。
-
我封裝的線程池工具類有什么好處?
使用線程安全的方式定義,整個項目不會重復創建線程池。線程池根據服務器cpu核數創建合適的核心線程數和最大線程數,達到不少創建不多創建剛剛好的配置。使用靜態方法調用,隨處可用,零難度上手。可以執行無返回值線程任務,可以執行有返回值的線程任務。
工具類
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*;
/**
* 自定義線程創建工具類,創建線程池后不需要關閉
*
* @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";
// 等待隊列長度
private static final int BLOCKING_QUEUE_LENGTH = 1000;
// 閑置線程存活時間
private static final int KEEP_ALIVE_TIME = 60 * 1000;
private ThreadPoolUtils() {
throw new IllegalStateException("utility class");
}
/**
* 無返回值直接執行
*
* @param runnable 需要運行的任務
*/
public static void execute(Runnable runnable) {
getThreadPool().execute(runnable);
}
/**
* 有返回值執行
* 主線程中使用Future.get()獲取返回值時,會阻塞主線程,直到任務執行完畢
*
* @param callable 需要運行的任務
*/
public static <T> Future<T> submit(Callable<T> callable) {
return getThreadPool().submit(callable);
}
private static synchronized ThreadPoolExecutor getThreadPool() {
if (threadPool == null) {
// 獲取處理器數量
int cpuNum = Runtime.getRuntime().availableProcessors();
// 根據cpu數量,計算出合理的線程并發數
int maximumPoolSize = cpuNum * 2 + 1;
// 核心線程數、最大線程數、閑置線程存活時間、時間單位、線程隊列、線程工廠、當前線程數已經超過最大線程數時的異常處理策略
threadPool = new ThreadPoolExecutor(maximumPoolSize - 1,
maximumPoolSize,
KEEP_ALIVE_TIME,
TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(BLOCKING_QUEUE_LENGTH),
new ThreadFactoryBuilder().setNameFormat(POOL_NAME + "-%d").build(),
new ThreadPoolExecutor.AbortPolicy() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
LOGGER.warn("線程爆炸了,當前運行線程總數:{},活動線程數:{}。等待隊列已滿,等待運行任務數:{}",
e.getPoolSize(),
e.getActiveCount(),
e.getQueue().size());
}
});
}
return threadPool;
}
}
例子1:
Future<String> future = ThreadPoolUtils.submit(() -> {
return "我有返回值哦";
});
try {
logger.info(future.get());
} catch (InterruptedException | ExecutionException e) {
logger.error("任務超過指定時間未返回值,線程超時退出");
}
?
// 控制臺打印日志:
21:04:19.428 [main] INFO - 我有返回值哦
例子2:
Future<String> futureTimeout = ThreadPoolUtils.submit(() -> {
Thread.sleep(99999999);
return "我有返回值,但是超時了";
});
try {
// 建議使用該方式執行任務,不會導致線程因為某寫原因一直占用線程,
// 從而導致未知問題
// 注意使用局部try避免主線程異常,導致主線程無法繼續執行
logger.info(futureTimeout.get(3, TimeUnit.SECONDS));
} catch (InterruptedException | ExecutionException e) {
logger.error("任務執行異常");
} catch (TimeoutException e) {
logger.error("任務超過指定時間未返回值,線程超時退出");
}
?
// 控制臺打印日志:
21:07:24.940 [main] ERROR - 任務超過指定時間未返回值,線程超時退出
例子3:
int loop = 40;
for (int i = 0; i < loop; i++) {
logger.info("任務{}", i);
ThreadPoolUtils.execute(() -> {
logger.info("干活好累");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("終于干完了");
});
}
logger.info("我在這兒等著你回來等你回來");
?
// 控制臺打印:
21:08:08.494 [main] INFO - 任務0
............
21:08:08.540 [main] INFO - 任務5
21:08:08.541 [main] INFO - 任務6
21: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 - 任務21
21:08:08.548 [main] INFO - 任務22
21:08:08.548 [main] INFO - 任務23
21:08:08.548 [myPool-21] INFO - 干活好累
21:08:08.549 [main] INFO - 任務24
21:08:08.549 [myPool-22] INFO - 干活好累
21:08:08.549 [main] INFO - 任務25
21:08:08.549 [myPool-23] INFO - 干活好累
21:08:08.549 [main] INFO - 任務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 - 任務35
21:08:08.551 [main] INFO - 任務36
21:08:08.551 [main] INFO - 任務37
21:08:08.551 [main] INFO - 任務38
21:08:08.551 [main] INFO - 任務39
21: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個線程使用工具類
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public 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 - 線程爆炸了,當前運行線程總數:33,活動線程數:33。等待隊列已滿,等待運行任務數:1000
21:13:25.085 [main] WARN - 線程爆炸了,當前運行線程總數:33,活動線程數:33。等待隊列已滿,等待運行任務數:1000
21:13:25.085 [main] WARN - 線程爆炸了,當前運行線程總數:33,活動線程數:33。等待隊列已滿,等待運行任務數:1000
21:13:25.085 [main] WARN - 線程爆炸了,當前運行線程總數:33,活動線程數:33。等待隊列已滿,等待運行任務數:1000
21:13:25.106 [myPool-7] INFO - 干活好累
21:13:25.106 [myPool-11] INFO - 干活好累
21:13:25.106 [main] WARN - 線程爆炸了,當前運行線程總數:33,活動線程數:33。等待隊列已滿,等待運行任務數:1000
21:13:25.106 [myPool-6] INFO - 干活好累
21:13:25.106 [myPool-4] INFO - 干活好累
21:13:25.106 [main] WARN - 線程爆炸了,當前運行線程總數:33,活動線程數:33。等待隊列已滿,等待運行任務數:1000
21:13:25.106 [main] WARN - 線程爆炸了,當前運行線程總數:33,活動線程數:33。等待隊列已滿,等待運行任務數:1000
............
鄙人編碼十年多,在項目中也積累了一些工具類,很多工具類在每個項目都有在用,很實用。大部分是鄙人封裝的,有些工具類是同事封裝的,有些工具類已經不記得是ctrl+c的還是自己封裝的了,現在有空就會總結項目中大部分的工具類,分享給各位碼友。如果文章中涉及的代碼有侵權行為請通知鄙人處理。
計劃是先把工具類整理出來,正所謂工欲善其事,必先利其器。項目中不管是普通單體項目還是多模塊maven項目或是分布式微服務,一部分功能模塊都是可以重用的,工具類模塊就是其中之一。

浙公網安備 33010602011771號