使用 hutool 的 ThreadUtil 执行多线程

10,157 阅读4分钟

最近有个小项目用到了线程池,因为不想重复造轮子,就直接用了 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;
   }
}