我的Java开发工具包之“线程池工具包”

115 阅读2分钟

abb3abcb2db24571b3a90c0ba09fc8d3.jpg 在开发过程中,我们一定会遇到使用线程池的场景,一个大一点的公司中项目数量一定会很多,线程池创建方式也会五花八门,那就不如规范一下,一次开发,终生受用。

第一步 定义一个自动装配线程池的注解

这里使用@Import({ThreadPool.class})注解向Spring容器中注入ThreadPool

/**
 * @Author: pp
 * @DateTime: 2022/1/5 11:46
 * @Description: TODO
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ThreadPool.class})
public @interface EnableThreadPool {
}

第二步 ThreadPool类实现

很简单,就是初始化线程池,然后实现一些线程池的使用方法,还有关闭服务的时候判断是否存在还在运行的线程,如果存在就等5秒,这个自行设置就好。

/**
 * @Author: pp
 * @DateTime: 2022/1/5 11:46
 * @Description: TODO
 */
public class ThreadPool {

    private static final Logger logger = LoggerFactory.getLogger(ThreadPool.class);

    private final ExecutorService executor;

    public ThreadPool(ThreadPoolProperties threadPoolProperties) {

        Integer corePoolSize = 30;
        Integer maximumPoolSize = 50;
        Integer linkedBlockingQueueSize = 10000;
        Integer keepAliveTime = 0;
        String rejectedExecutionHandler = "CallerRunsPolicy";
        if (threadPoolProperties != null) {
            if (threadPoolProperties.getCorePoolSize() != null && threadPoolProperties.getCorePoolSize() > 0) {
                corePoolSize = threadPoolProperties.getCorePoolSize();
            }
            if (threadPoolProperties.getMaximumPoolSize() != null && threadPoolProperties.getMaximumPoolSize() > 0) {
                maximumPoolSize = threadPoolProperties.getMaximumPoolSize();
                if (corePoolSize > maximumPoolSize) {
                    logger.error("maximumPoolSize apply corePoolSize, because thread pool corePoolSize less than maximumPoolSize, need modification corePoolSize or maximumPoolSize");
                    maximumPoolSize = corePoolSize;
                }
            }
            if (threadPoolProperties.getLinkedBlockingQueueSize() != null && threadPoolProperties.getLinkedBlockingQueueSize() > 0) {
                linkedBlockingQueueSize = threadPoolProperties.getLinkedBlockingQueueSize();
            }
            if (threadPoolProperties.getKeepAliveMillisecondsTime() != null && threadPoolProperties.getKeepAliveMillisecondsTime() > 0) {
                keepAliveTime = threadPoolProperties.getKeepAliveMillisecondsTime();
            }
            if (threadPoolProperties.getRejectedExecutionHandler() != null && StringUtils.isNotEmpty(threadPoolProperties.getRejectedExecutionHandler())) {
                rejectedExecutionHandler = threadPoolProperties.getRejectedExecutionHandler();
            }

        }

        logger.info(String.format("thread pool configuration {corePoolSize:%s  maximumPoolSize:%s  linkedBlockingQueueSize:%s  keepAliveTime:%s  rejectedExecutionHandler:%s}",
                corePoolSize, maximumPoolSize, linkedBlockingQueueSize, keepAliveTime, rejectedExecutionHandler));

        executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                keepAliveTime, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(linkedBlockingQueueSize),
                getRejectedExecutionHandler(rejectedExecutionHandler));

        logger.info("===============thread pool initialization completed !===============");

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            logger.info("===============thread pool destruction !===============");
            executor.shutdown();
            try {
                if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                    executor.shutdownNow();
                }
            } catch (InterruptedException e) {
                executor.shutdownNow();
            }
        }));

    }

    public <T> Future<T> submit(Callable<T> callable) {
        return executor.submit(callable);
    }

    public void submit(ExecutorInterface i) {
        executor.submit(() -> {
            try {
                i.run();
            } catch (Exception e) {
                logger.error("", e);
            }
        });
    }

    public void allOfTask(List<Runnable> tasks, long timeout, TimeUnit unit) {
        List<CompletableFuture<Void>> list = tasks.stream()
                .map(runnable -> CompletableFuture.runAsync(runnable, executor)
                        .exceptionally(throwable -> {
                            logger.error("", throwable);
                            return null;
                        })
                )
                .collect(Collectors.toList());

        CompletableFuture<Void> all = CompletableFuture.allOf(list.toArray(new CompletableFuture[0]));
        try {
            all.get(timeout, unit);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            logger.info("all of task error. e: {}, message: {}", e.getClass().getName(), e.getMessage());
        }

        List<CompletableFuture<Void>> r = list.stream().filter(future -> !future.isDone()).collect(Collectors.toList());
        logger.info("all of task result. task count: {}, fail count: {}", tasks.size(), r.size());
    }

    private RejectedExecutionHandler getRejectedExecutionHandler(String rejectedExecutionHandler) {
        switch (rejectedExecutionHandler) {
            case "AbortPolicy":
                return new ThreadPoolExecutor.AbortPolicy();
            case "DiscardPolicy":
                return new ThreadPoolExecutor.DiscardPolicy();
            case "DiscardOldestPolicy":
                return new ThreadPoolExecutor.DiscardOldestPolicy();
            default:
                return new ThreadPoolExecutor.CallerRunsPolicy();
        }
    }

}

第三步 ThreadPoolProperties类实现

作用就是从配置文件中获取线程池配置信息

/**
 * @Author: pp
 * @DateTime: 2022/1/14 15:38
 * @Description: TODO
 */
@Component
@ConfigurationProperties(prefix = "md.thread-pool")
@Data
public class ThreadPoolProperties {

    private Integer corePoolSize;

    private Integer maximumPoolSize;

    private Integer linkedBlockingQueueSize;

    private Integer keepAliveMillisecondsTime;

    private String rejectedExecutionHandler;

    public void setCorePoolSize(Integer corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public void setMaximumPoolSize(Integer maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }

    public void setLinkedBlockingQueueSize(Integer linkedBlockingQueueSize) {
        this.linkedBlockingQueueSize = linkedBlockingQueueSize;
    }

    public void setKeepAliveMillisecondsTime(Integer keepAliveMillisecondsTime) {
        this.keepAliveMillisecondsTime = keepAliveMillisecondsTime;
    }

    public void setRejectedExecutionHandler(String rejectedExecutionHandler) {
        this.rejectedExecutionHandler = rejectedExecutionHandler;
    }



}

第四步 ExecutorInterface类实现

被 @FunctionalInterface注解修饰的函数接口

/**
 * @Author: pp
 * @DateTime: 2022/1/5 11:42
 * @Description:
 */

@FunctionalInterface
public interface ExecutorInterface {

    void run();

}

第五步 使用

  1. 我们将这些代码放到我们的公共包中,打包成jar,在我们的项目中引用
  2. 配置线程池参数,如果没配置就会使用默认参数

截屏2024-01-05 14.02.06.png 3. 启动类上添加 @EnableThreadPool 注解

截屏2024-01-05 14.04.39.png 4. 使用

截屏2024-01-05 14.05.32.png

threadPool.submit(()->{
    
});

工具中封装了多线程池批量处理,具体使用场景在juejin.cn/post/731241… 有提到

后续会陆续分享好用的工具方法