线程池UncaughtExceptionHandler无效?可能是使用方式不对

news2024/12/22 15:08:10

背景

在业务处理中,使用了线程池来提交任务执行,但是今天修改了一小段代码,发现任务未正确执行。而且看了相关日志,也并未打印结果。

源码简化版如下:
首先,自定义了一个线程池

public class NamedThreadFactory implements ThreadFactory {

    private final AtomicInteger threadNumber = new AtomicInteger(1);
    
    private final String namePrefix;
    
    private final ThreadGroup group;
    
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    public NamedThreadFactory(String namePrefix) {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        this.namePrefix = namePrefix + "-thread-";
    }
    
    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
        t.setUncaughtExceptionHandler(new ThreadUncaugthExceptionHandler());
        return t;
    }
    
    private class ThreadUncaugthExceptionHandler implements UncaughtExceptionHandler {
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            logger.error("uncaughtException thead name:{}, msg:{}", t.getName(), e.getMessage(), e);
        }
    
    }
}

线程池A如下所示

ThreadPoolExecutor EXECUTOR_A = 
new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, 
                       new ArrayBlockingQueue<Runnable>(100),
                       new NamedThreadFactory("AService-"));

待执行任务

EXECUTOR_A.submit(() -> {
    // 处理step1
    ......
    // 以下是本次新增代码
    ZoneId zoneId = ZoneId.of("Asia/Shanghai");
    LocalDate now = LocalDate.now(zoneId);
    LocalDate endTime = now.plus(1, ChronoUnit.YEARS);
    //final变量DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String endTIme = DATE_TIME_FORMATTER.format(endTime);
    // 调用B的处理方法
    
});

对于上述新增的代码,会报以下异常

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay

这是因为希望格式化的是yyyy-MM-dd HH:mm:ss格式,我使用的是LocalDate,实际应该使用LocalDateTime才对。

解析

从背景中可以看到新增的代码由于书写错误,会报异常。

同时由于exeuctor提交的Runnable任务中缺少try-catch相应处理,那么该任务会执行失败。但是这里有一个奇怪的地方,明明给线程池自定了ThreadFactory,并且指定了UncaughtExceptionHandler,里面应该会打印错误日志才对。

可是翻遍了日志,却一点没有找到。
到这里有一些朋友可能已经知道问题了,问题的关键就在于任务提交的方式,也就是submit和execute的差异。

概况一下,在Executor框架中,线程池提供了两个方法用于提交任务:execute()和submit()。这两个方法的主要区别如下:

  1. execute()方法:
    • 用于提交不需要返回值的任务,即Runnable类型的任务。
    • execute()方法将任务提交给线程池后,将立即返回,而不等待任务执行完成或返回结果。
    • 如果任务内部发生异常,线程池会捕获并抛出异常。
  2. submit()方法:
    • 用于提交需要返回值的任务,即Callable类型的任务,也可以执行Runnable,会以Void作为返回类型。
    • submit()方法将任务提交给线程池后,返回一个Future对象,可以使用该对象的get()方法获取任务执行的结果。
    • 如果任务内部发生异常,线程池会将异常封装在ExecutionException中,通过Future对象的get()方法处理抛出的ExecutionException。

对于execute方法中的异常处理,可以查看以下代码,红框中是对于RuntimeException直接抛出。

java.util.concurrent.ThreadPoolExecutor#runWorker

image.png
而对于submit方法来说,任务提交的时候,会创建一个FutureTask。

image.png

image.png
FutureTask的run方法处理如下

image.png
在异常情况下,将异常赋值给了outcome。

image.png
而当我们调用了Future.get()方法时,

image.png

image.png
综上分析,如果是execute方式提交任务,异常会直接抛出,最终进入到自定义的UncaughtExceptionHandler。如果是submit方式提交任务,异常只会在Future.get()方法时抛出,如果并没有调用get方法,那么是不会感知到异常的。此时也就是本文中的情况,就无法看到自定义的UncaughtExceptionHandler打印的日志了。

总结

推荐的处理方式

  • 推荐try-catch对线程任务进行异常捕获
  • 推荐自定义ThreadFacory,并自定义UncaughtExceptionHandler进行异常打印,避免有一些异常捕获遗漏的情况。当然此场景下,一定要区分submit和execute任务提交方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/914221.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iMX6ULL QT环境配置 | CMake在Linux下的交叉编译环境搭建及使用

习惯了使用cmake&#xff0c;再也不想回到手写makefile的年代了。相比手写makefile&#xff0c;使用cmake则像是实现了机动化&#xff0c;管理项目工程的编译变得很简单了。况且cmake很流行&#xff0c;linux下的很多软件源码包&#xff0c;很多也都使用了cmake的方式编译。因此…

大数据课程K4——Spark的DAGRDD依赖关系

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的DAG; ⚪ 掌握Spark的RDD的依赖关系; ⚪ 了解Spark对于DAG的Stage的划分; 一、DAG概念 1. 概述 Spark会根据用户提交的计算逻辑中的RDD的转换和动作来生成RDD之间的依赖关…

Android JNI系列详解之AS创建Native C++项目

一、前提 Android Studio版本&#xff1a;Android Studio Electric Eel | 2022.1.1 Patch 1 二、创建Native C项目 1.新建项目 2.选择新建Native C项目 3.New Project 4.选择C标准库的支持版本 5.项目自带的默认生成的代码 6.buil.gradle中也自带了CMakeList的配置&#xff08;…

详细了解G1、了解G1、G1垃圾收集器详解、G1垃圾回收器简单调优

4.详细了解G1&#xff1a; 4.1.一&#xff1a;什么是垃圾回收 4.2.了解G1 4.3.G1 Yong GC 4.4.G1 Mix GC 4.5.三色标记算法 4.6.调优实践 5.G1垃圾收集器详解 5.1.G1垃圾收集器 5.2.G1的堆内存划分 5.3.G1的运行过程 5.4.三色标记 5.4.1.漏标问题 5.5.记忆集与卡表 5.6.安全点与…

开发小技巧(逐步完善)

一、验证码 1&#xff09;将 大小写字母 和 数字 存储在字符数组中 2&#xff09;用随机数的方式生成随机码 3&#xff09;采用字符串的方式存储验证码即可 import java.util.Random;public class TEST {public static void main(String[] args) {char[] chs new char[62];//…

小程序中的全局配置以及常用的配置项(window,tabBar)

全局配置文件和常用的配置项 app.json: pages:是一个数组&#xff0c;用于记录当前小程序所有页面的存放路径&#xff0c;可以通过它来创建页面 window:全局设置小程序窗口的外观(导航栏&#xff0c;背景&#xff0c;页面的主体) tabBar:设置小程序底部的 tabBar效果 style:是否…

存储IO路径:Linux下的“快递之旅”

相信大家都有过网购的经历,当我们在电商平台上浏览心仪的商品并下单时,快递小哥会负责将物品从商家手中送至我们手中。在这个过程中,快递小哥需要经过一系列的流程才能将物品准确送达。同样,在Linux系统中,当用户下发一笔读写操作时,这些数据也需要经过一系列的流程才能最…

uniapp 回退到指定页面 保存页面状态

uniapp 历史页面回退到指定页面。 getCurrentPages() 内容如下 let delta getCurrentPages().reverse().findIndex(item > item.route "pages/popularScience/daodi") if(delta-1){uni.navigateTo({url: /pages/popularScience/daodi,success: res > {},fa…

【数据结构练习】链表面试题锦集一

目录 前言&#xff1a; 1. 删除链表中所有值为key的节点 方法一&#xff1a;正常删除&#xff0c;头结点另外讨论 方法二:虚拟头结点法 方法三&#xff1a;递归 2.反转链表 方法一&#xff1a;双指针迭代 方法二&#xff1a;递归法解析&#xff1a; 3.链表的中间结点 方法…

动态规划:删除并获得点数

题目来源&#xff1a;删除并获得点数 题目分析 题目分析&#xff1a; 从题目中可以获取到的条件是&#xff0c;如果选择了i位置&#xff0c;那么就必须删除与i-1和i1的位置的值相同的所有的值。 既然要删除相同的值&#xff0c;那么我们可以想&#xff0c;要不要先排序&…

捕捉现货白银短期波动办法

有的投资者认为&#xff0c;在现货白银市场中&#xff0c;投资最适合的就是使用短线投资方法。因为短线投资可以充分发挥现货白银波动灵活的特点&#xff0c;尤其是对资金量少的投资者更是如此。确实&#xff0c;现货白银确实是比较适合进行短线投资&#xff0c;那么下面我们就…

图像检索,目标检测map的实现

一、图像检索指标Rank1,map 参考&#xff1a;https://blog.csdn.net/weixin_41427758/article/details/81188164?spm1001.2014.3001.5506 1.Rank1: rank-k&#xff1a;算法返回的排序列表中&#xff0c;前k位为存在检索目标则称为rank-k命中。 常用的为rank1&#xff1a;首…

Linux: 使用 ssh 连接其他服务器

通过ifconfig 查看要连接的服务器地址&#xff1a; ubuntuubuntu1804-0172:/media/sangfor/vdc$ ssh ubuntu192.168.11.49 输入要连接的服务器密码: ubuntua192.168.1149 s password: 连接服务器成功&#xff1a;

二、pikachu之SQL注入(2)

文章目录 1、delete注入2、http header注入3、布尔盲注4、时间盲注 4、宽字节注入 1、delete注入 &#xff08;1&#xff09;寻找传参页面&#xff0c;在删除留言的时候&#xff0c;发现是get传参&#xff1b; &#xff08;2&#xff09;判断是否存在注入点&#xff0c;命令&…

创建Ingress实例

部署deployment和service apiVersion: apps/v1 kind: Deployment metadata:creationTimestamp: nulllabels:app: webname: webnamespace: dalong spec:replicas: 2selector:matchLabels:app: webstrategy: {}template:metadata:creationTimestamp: nullspec:containers:- ima…

LabVIEW硬件在环仿真模拟电路故障分析和特征提取

LabVIEW硬件在环仿真模拟电路故障分析和特征提取 与数字电路相比&#xff0c;模拟电路故障分析是一项具有挑战性的任务。这主要是由于模拟分立元件的非线性特性&#xff0c;以及其他因素&#xff0c;包括噪声和内部可访问性的限制。参数故障和灾难性故障是模拟电路中发生的两种…

写之前的项目关于使用git remote -v 找不到项目地址的解决方案

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、报错解析1. 报错内容2. 报错翻译3. 报错解析&#xff08;1&#xff09;使用git branch来查看git仓库有几个分支&#xff08;2&#xff09;使用git remote -v&am…

Linux 发行版 Debian 宣布支持龙芯 LoongArch 架构

近期&#xff0c;龙芯发布了 3A6000 桌面处理器&#xff0c;芯片的性能又一次大幅度提升&#xff0c;成为国产芯片的又一里程碑。 同期&#xff0c;LoongArch 架构的生态建设也迅速提升&#xff0c;开源网络引导固件 iPXE、QQ Linux 版、摩尔线程等软硬件都官宣支持龙芯 Loong…

SpringBoot生成和解析二维码完整工具类分享(提供Gitee源码)

前言&#xff1a;在日常的开发工作当中可能需要实现一个二维码小功能&#xff0c;我参考了网上很多关于SpringBoot生成二维码的教程&#xff0c;最终还是自己封装了一套完整生成二维码的工具类&#xff0c;可以支持基础的黑白二维码、带颜色的二维码、带Logo的二维码、带颜色和…

【考研数学】线形代数第三章——向量 | 基本概念、向量组的相关性与线性表示

文章目录 引言一、向量的概念与运算1.1 基本概念1.2 向量运算的性质 二、向量组的相关性与线性表示2.1 理论背景2.2 相关性与线性表示基本概念2.3 向量组相关性与线性表示的性质 引言 向量是线性代数的重点和难点。向量是矩阵&#xff0c;同时矩阵又是由向量构成的&#xff0c…