「Java工具类」java线程池工具类,自定义线程池工具类

阅读(487)
评论(0)
Beginning

介绍语

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

Ending
本文如果对您有帮助欢迎打赏作者,多少随意一分也是爱!

作者:Java夜未眠

出处:https://www.cnblogs.com/liangxianning/p/17058248.html

版权声明:本博客所有文章除特别声明外,均采用「 MIT 许可协议。」许可协议进行许可

关于博主: 评论和私信会可能回复较慢,点击上面加人图标加我为好友吧

posted @   Java夜未眠  阅读(487)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
more_horiz
keyboard_arrow_up light_mode
选择主题
点击右上角即可分享
微信分享提示