介绍语
本博客主要是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项目或是分布式微服务,一部分功能模块都是可以重用的,工具类模块就是其中之一。
|