diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java index 16f309d8f..18d3614ae 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java @@ -5,15 +5,12 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.dromara.common.core.config.properties.ThreadPoolProperties; import org.dromara.common.core.utils.SpringUtils; -import org.dromara.common.core.utils.Threads; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.core.task.VirtualThreadTaskExecutor; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.*; /** * 线程池配置 @@ -50,7 +47,7 @@ public class ThreadPoolConfig { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); - Threads.printException(r, t); + printException(r, t); } }; this.scheduledExecutorService = scheduledThreadPoolExecutor; @@ -59,15 +56,57 @@ public class ThreadPoolConfig { /** * 销毁事件 + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. */ @PreDestroy public void destroy() { try { log.info("====关闭后台任务任务线程池===="); - Threads.shutdownAndAwaitTermination(scheduledExecutorService); + ScheduledExecutorService pool = scheduledExecutorService; + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + log.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } } catch (Exception e) { log.error(e.getMessage(), e); } } + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + log.error(t.getMessage(), t); + } + } + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java deleted file mode 100644 index 16b352c3a..000000000 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.dromara.common.core.utils; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.*; - -/** - * 线程相关工具类. - * - * @author ruoyi - */ -@Slf4j -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class Threads { - - /** - * 停止线程池 - * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. - * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. - * 如果仍然超時,則強制退出. - * 另对在shutdown时线程本身被调用中断做了处理. - */ - public static void shutdownAndAwaitTermination(ExecutorService pool) { - if (pool != null && !pool.isShutdown()) { - pool.shutdown(); - try { - if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { - pool.shutdownNow(); - if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { - log.info("Pool did not terminate"); - } - } - } catch (InterruptedException ie) { - pool.shutdownNow(); - Thread.currentThread().interrupt(); - } - } - } - - /** - * 打印线程异常信息 - */ - public static void printException(Runnable r, Throwable t) { - if (t == null && r instanceof Future) { - try { - Future future = (Future) r; - if (future.isDone()) { - future.get(); - } - } catch (CancellationException ce) { - t = ce; - } catch (ExecutionException ee) { - t = ee.getCause(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } - } - if (t != null) { - log.error(t.getMessage(), t); - } - } - - /** - * 获取异常的根因(递归查找) - * - * @param e 当前异常 - * @return 根因异常(最底层的 cause) - *

- * 逻辑说明: - * 1. 如果 e 没有 cause,说明 e 本身就是根因,直接返回 - * 2. 如果 e 的 cause 和自身相同(防止循环引用),也返回 e - * 3. 否则递归调用,继续向下寻找最底层的 cause - */ - public static Throwable getRootCause(Throwable e) { - Throwable cause = e.getCause(); - if (cause == null || cause == e) { - return e; - } - return getRootCause(cause); - } - - /** - * 在异常链中查找指定类型的异常 - * - * @param e 当前异常 - * @param clazz 目标异常类 - * @return 找到的指定类型异常,如果没有找到返回 null - */ - public static Throwable findCause(Throwable e, Class clazz) { - Throwable t = e; - while (t != null && t != t.getCause()) { - if (clazz.isInstance(t)) { - return t; - } - t = t.getCause(); - } - return null; - } - -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java index 7fa2bffaa..094785bd7 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java @@ -6,7 +6,6 @@ import com.baomidou.dynamic.datasource.exception.CannotFindDataSourceException; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; -import org.dromara.common.core.utils.Threads; import org.mybatis.spring.MyBatisSystemException; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -37,7 +36,7 @@ public class MybatisExceptionHandler { @ExceptionHandler(MyBatisSystemException.class) public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); - Throwable root = Threads.getRootCause(e); + Throwable root = getRootCause(e); if (root instanceof NotLoginException) { log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, root.getMessage()); return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); @@ -50,4 +49,41 @@ public class MybatisExceptionHandler { return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()); } + /** + * 获取异常的根因(递归查找) + * + * @param e 当前异常 + * @return 根因异常(最底层的 cause) + *

+ * 逻辑说明: + * 1. 如果 e 没有 cause,说明 e 本身就是根因,直接返回 + * 2. 如果 e 的 cause 和自身相同(防止循环引用),也返回 e + * 3. 否则递归调用,继续向下寻找最底层的 cause + */ + public static Throwable getRootCause(Throwable e) { + Throwable cause = e.getCause(); + if (cause == null || cause == e) { + return e; + } + return getRootCause(cause); + } + + /** + * 在异常链中查找指定类型的异常 + * + * @param e 当前异常 + * @param clazz 目标异常类 + * @return 找到的指定类型异常,如果没有找到返回 null + */ + public static Throwable findCause(Throwable e, Class clazz) { + Throwable t = e; + while (t != null && t != t.getCause()) { + if (clazz.isInstance(t)) { + return t; + } + t = t.getCause(); + } + return null; + } + }