在开发过程中,我们一定会遇到使用线程池的场景,一个大一点的公司中项目数量一定会很多,线程池创建方式也会五花八门,那就不如规范一下,一次开发,终生受用。
第一步 定义一个自动装配线程池的注解
这里使用@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();
}
第五步 使用
- 我们将这些代码放到我们的公共包中,打包成jar,在我们的项目中引用
- 配置线程池参数,如果没配置就会使用默认参数
3. 启动类上添加 @EnableThreadPool 注解
4. 使用
threadPool.submit(()->{
});
工具中封装了多线程池批量处理,具体使用场景在juejin.cn/post/731241… 有提到
后续会陆续分享好用的工具方法