最近有个小项目用到了线程池,因为不想重复造轮子,就直接用了 hutool 包里的 ThreadUtil,目前没发现问题,也确实方便,下面是我的使用方式,供参考。
- 异步线程
@Override
public Future<?> asyncUserDetail(Integer threadCount) {
if (threadCount == null || threadCount <= 0) {
threadCount = 1;
}
StringBuilder msg = new StringBuilder();
//同步用户详情,开启两个线程
Future<?> future = null;
for (int i = 0; i < threadCount; i++) {
String threadName = "userdetail-data-sync-" + i;
if (!checkThreadAlive(threadName)) {
future = ThreadUtil.execAsync(() -> {
StopWatch watch = new StopWatch();
watch.start(threadName + "异步用户详情数据同步");
Thread.currentThread().setName(threadName);
importUserdetailForQueue();
watch.stop();
log.info("用户详情数据同步完成(asyncUserDetail):{} --> 耗时:{} ms", threadName, watch.getTotalTimeMillis());
});
msg.append("用户列表详情,线程").append(threadName).append("-开始同步……\n");
} else {
msg.append("用户列表详情线程").append(threadName).append("-正在同步,请稍后重试……\n");
}
}
log.info("用户详情数据同步:\n {}", msg);
return future;
}
//通过线程名称检查线程任务是否已开启,防止重复执行
private boolean checkThreadAlive(String taskName) {
Thread[] threads = ThreadUtil.getThreads();
long count = Arrays.stream(threads).filter(item -> item.getName().equals(taskName)).count();
return count > 0;
}
//调用示例
@Override
public Boolean asyncDataBlocking(String timestamp) {
//默认值
if (StrUtil.isBlank(timestamp)) {
//从三天前开始同步
Date threeDaysAgo = DateUtil.offsetDay(DateUtil.date(), -3);
timestamp = DateUtil.format(threeDaysAgo, DatePattern.NORM_DATETIME_PATTERN);
}
StopWatch watch = new StopWatch();
watch.start("异步阻塞调用-数据同步");
try {
//同步部门
Future<?> orgouFuture = asyncOrgouinfo(timestamp);
//同步用户列表
Future<?> userprofileFuture = asyncUserprofile(timestamp);
TimeUnit.SECONDS.sleep(2);
//同步用户详情,开启两个线程
Future<?> userDetailFuture = asyncUserDetail(1);
while (!orgouFuture.isDone() || !userprofileFuture.isDone() || !userDetailFuture.isDone()) {
TimeUnit.SECONDS.sleep(10);
}
} catch (InterruptedException e) {
log.error("线程异常:{}", e.getMessage(), e);
return false;
} catch (Exception e) {
log.error("未知异常:{}", e.getMessage(), e);
return false;
}finally {
watch.stop();
}
log.info("数据同步完成(asyncDataBlocking)。ts: {},{}, 耗时:{}ms", timestamp, watch.getLastTaskName(), watch.getTotalTimeMillis());
return true;
}
顺带提一下 hutool ThreadUtil 线程池的源码:cn.hutool.core.thread.ExecutorBuilder
源码示例:
/**
* {@link ThreadPoolExecutor} 建造者
*
* @author looly
* @since 4.1.9
*/
public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
private static final long serialVersionUID = 1L;
/** 默认的等待队列容量 */
public static final int DEFAULT_QUEUE_CAPACITY = 1024;
/**
* 初始池大小
*/
private int corePoolSize;
/**
* 最大池大小(允许同时执行的最大线程数)
*/
private int maxPoolSize = Integer.MAX_VALUE;
/**
* 线程存活时间,即当池中线程多于初始大小时,多出的线程保留的时长
*/
private long keepAliveTime = TimeUnit.SECONDS.toNanos(60);
/**
* 队列,用于存在未执行的线程
*/
private BlockingQueue<Runnable> workQueue;
/**
* 线程工厂,用于自定义线程创建
*/
private ThreadFactory threadFactory;
/**
* 当线程阻塞(block)时的异常处理器,所谓线程阻塞即线程池和等待队列已满,无法处理线程时采取的策略
*/
private RejectedExecutionHandler handler;
/**
* 线程执行超时后是否回收线程
*/
private Boolean allowCoreThreadTimeOut;
/**
* 设置初始池大小,默认0
*
* @param corePoolSize 初始池大小
* @return this
*/
public ExecutorBuilder setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
return this;
}
/**
* 设置最大池大小(允许同时执行的最大线程数)
*
* @param maxPoolSize 最大池大小(允许同时执行的最大线程数)
* @return this
*/
public ExecutorBuilder setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
return this;
}
/**
* 设置线程存活时间,即当池中线程多于初始大小时,多出的线程保留的时长
*
* @param keepAliveTime 线程存活时间
* @param unit 单位
* @return this
*/
public ExecutorBuilder setKeepAliveTime(long keepAliveTime, TimeUnit unit) {
return setKeepAliveTime(unit.toNanos(keepAliveTime));
}
/**
* 设置线程存活时间,即当池中线程多于初始大小时,多出的线程保留的时长,单位纳秒
*
* @param keepAliveTime 线程存活时间,单位纳秒
* @return this
*/
public ExecutorBuilder setKeepAliveTime(long keepAliveTime) {
this.keepAliveTime = keepAliveTime;
return this;
}
/**
* 设置队列,用于存在未执行的线程<br>
* 可选队列有:
*
* <pre>
* 1. {@link SynchronousQueue} 它将任务直接提交给线程而不保持它们。当运行线程小于maxPoolSize时会创建新线程,否则触发异常策略
* 2. {@link LinkedBlockingQueue} 默认无界队列,当运行线程大于corePoolSize时始终放入此队列,此时maximumPoolSize无效。
* 当构造LinkedBlockingQueue对象时传入参数,变为有界队列,队列满时,运行线程小于maxPoolSize时会创建新线程,否则触发异常策略
* 3. {@link ArrayBlockingQueue} 有界队列,相对无界队列有利于控制队列大小,队列满时,运行线程小于maxPoolSize时会创建新线程,否则触发异常策略
* </pre>
*
* @param workQueue 队列
* @return this
*/
public ExecutorBuilder setWorkQueue(BlockingQueue<Runnable> workQueue) {
this.workQueue = workQueue;
return this;
}
/**
* 使用{@link ArrayBlockingQueue} 做为等待队列<br>
* 有界队列,相对无界队列有利于控制队列大小,队列满时,运行线程小于maxPoolSize时会创建新线程,否则触发异常策略
*
* @param capacity 队列容量
* @return this
* @since 5.1.4
*/
public ExecutorBuilder useArrayBlockingQueue(int capacity) {
return setWorkQueue(new ArrayBlockingQueue<>(capacity));
}
/**
* 使用{@link SynchronousQueue} 做为等待队列(非公平策略)<br>
* 它将任务直接提交给线程而不保持它们。当运行线程小于maxPoolSize时会创建新线程,否则触发异常策略
*
* @return this
* @since 4.1.11
*/
public ExecutorBuilder useSynchronousQueue() {
return useSynchronousQueue(false);
}
/**
* 使用{@link SynchronousQueue} 做为等待队列<br>
* 它将任务直接提交给线程而不保持它们。当运行线程小于maxPoolSize时会创建新线程,否则触发异常策略
*
* @param fair 是否使用公平访问策略
* @return this
* @since 4.5.0
*/
public ExecutorBuilder useSynchronousQueue(boolean fair) {
return setWorkQueue(new SynchronousQueue<>(fair));
}
/**
* 设置线程工厂,用于自定义线程创建
*
* @param threadFactory 线程工厂
* @return this
* @see ThreadFactoryBuilder
*/
public ExecutorBuilder setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
return this;
}
/**
* 设置当线程阻塞(block)时的异常处理器,所谓线程阻塞即线程池和等待队列已满,无法处理线程时采取的策略
* <p>
* 此处可以使用JDK预定义的几种策略,见{@link RejectPolicy}枚举
*
* @param handler {@link RejectedExecutionHandler}
* @return this
* @see RejectPolicy
*/
public ExecutorBuilder setHandler(RejectedExecutionHandler handler) {
this.handler = handler;
return this;
}
/**
* 设置线程执行超时后是否回收线程
*
* @param allowCoreThreadTimeOut 线程执行超时后是否回收线程
* @return this
*/
public ExecutorBuilder setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
return this;
}
/**
* 创建ExecutorBuilder,开始构建
*
* @return {@link ExecutorBuilder}
*/
public static ExecutorBuilder create() {
return new ExecutorBuilder();
}
/**
* 构建ThreadPoolExecutor
*/
@Override
public ThreadPoolExecutor build() {
return build(this);
}
/**
* 创建有回收关闭功能的ExecutorService
*
* @return 创建有回收关闭功能的ExecutorService
* @since 5.1.4
*/
public ExecutorService buildFinalizable() {
return new FinalizableDelegatedExecutorService(build());
}
/**
* 构建ThreadPoolExecutor
*
* @param builder {@link ExecutorBuilder}
* @return {@link ThreadPoolExecutor}
*/
private static ThreadPoolExecutor build(ExecutorBuilder builder) {
final int corePoolSize = builder.corePoolSize;
final int maxPoolSize = builder.maxPoolSize;
final long keepAliveTime = builder.keepAliveTime;
final BlockingQueue<Runnable> workQueue;
if (null != builder.workQueue) {
workQueue = builder.workQueue;
} else {
// corePoolSize为0则要使用SynchronousQueue避免无限阻塞
workQueue = (corePoolSize <= 0) ? new SynchronousQueue<>() : new LinkedBlockingQueue<>(DEFAULT_QUEUE_CAPACITY);
}
final ThreadFactory threadFactory = (null != builder.threadFactory) ? builder.threadFactory : Executors.defaultThreadFactory();
RejectedExecutionHandler handler = ObjectUtil.defaultIfNull(builder.handler, new ThreadPoolExecutor.AbortPolicy());
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(//
corePoolSize, //
maxPoolSize, //
keepAliveTime, TimeUnit.NANOSECONDS, //
workQueue, //
threadFactory, //
handler//
);
if (null != builder.allowCoreThreadTimeOut) {
threadPoolExecutor.allowCoreThreadTimeOut(builder.allowCoreThreadTimeOut);
}
return threadPoolExecutor;
}
}