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

背景

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

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

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.mfbz.cn/a/86360.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

视频批量剪辑矩阵分发系统源码开源分享----基于PHP语言

批量剪辑视频矩阵分发&#xff1a; 短视频seo主要基于抖音短视频平台&#xff0c;为企业实现多账号管理&#xff0c;视频分发&#xff0c;视频批量剪辑&#xff0c;抖音小程序搭建&#xff0c;企业私域转化等&#xff0c;本文主要介绍短视频矩阵系统抖音小程序开发详细及注意事…

selenium +Jmeter 的性能测试

通过Jmeter快速将已有的Selenium 代码以性能测试的方式组织起来&#xff0c;并使用JMeter 丰富的报表展示测试结果 from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By driver …

孟羽童:成为勇敢逆袭的职场女性

相信职场中的小伙伴几乎都听过孟羽童这个名字&#xff0c;她从籍籍无名的应届毕业生到现如今摇身一变职场女强人&#xff0c;相信一定有职场人值得借鉴的地方。那么今天小编就给大家分享一下孟羽童的职场经历带给我的启发。 该图片来自网络&#xff0c;侵删 1、年龄并不是成功的…

java八股文面试[java基础]——final 关键字作用

为什么局部内部类和匿名内部类只能访问final变量&#xff1a; 知识来源 【基础】final_哔哩哔哩_bilibili

【Redis】Redis 的主从同步

【Redis】Redis 的主从同步 很多企业都没有使用 Redis 的集群&#xff0c;但是至少都做了主从。有了主从&#xff0c;当主节点(Master) 挂掉的时候&#xff0c;运维让从节点 (Slave) 过来接管&#xff0c;服务就可以继续&#xff0c;否则主节点需要经过数据恢复和重启的过程&a…

xcode15 change

jump to define 由原先的 control command left click 改为command left click

NLP与大模型主题全国师资培训班落地,飞桨持续赋能AI人才培养

为了推动大模型及人工智能相关专业人员的培养&#xff0c;8月11日-8月13日&#xff0c;由中国计算机学会主办、机械工业出版社、北京航空航天大学、百度飞桨联合承办 “CCF群星计划之文心高校行- NLP与大模型”主题师资培训班&#xff08;以下简称培训班&#xff09;在北京天信…

怎样压缩mp4视频大小?

怎样压缩mp4视频大小&#xff1f;由于视频文件的体积通常比其他类型的文件更大&#xff0c;因此它们需要更多的存储空间来保存。但是&#xff0c;如果我们的设备、应用程序或平台不支持某些视频格式或分辨率&#xff0c;或者我们没有足够的存储空间来容纳这些大型视频文件&…

产品流程图是什么?怎么做?

产品流程图是什么&#xff1f; 产品流程图是一种图形化的表达方式&#xff0c;用于描述产品开发、制造、销售、使用等各个阶段中涉及的流程、步骤和关系。它通过图形符号、箭头、文本等元素&#xff0c;展示了产品的各个环节之间的关联和顺序&#xff0c;通常被用于可视化产…

Vivado2018的工程迁移到Vivado2019上

Vivado2018的工程迁移到Vivado2019上 说明&#xff1a; 迁移很简单直接打开以后直接更新IP核后&#xff0c;即可重新编译工程 1、打开Vivado2019软件&#xff0c;准备打开工程 2、更新IP核 3、重新编译即可

Cesium.Entity图片纹理在不同观察角度有不同亮度

Cesium.Entity图片纹理在不同观察角度有不同亮度 测试代码&#xff1a; viewer.entities.add({rectangle: {coordinates: Cesium.Rectangle.fromDegrees(-92.0, 30.0, -76.0, 40.0),material: "../images/rect.png",} }); 测试图片&#xff1a; rect.png 这个图片…

制作一个专属于安防监控业的小程序商城

随着科技的发展和人们生活水平的提高&#xff0c;安防监控设备在我们的日常生活中起到了越来越重要的作用。因此&#xff0c;建立一个安防监控设备商城小程序就变得尤为重要。下面将介绍如何建立这样一个小程序。 第一步&#xff0c;登录乔拓云平台后台&#xff0c;进入商城管理…

智慧工地:安防监控EasyCVR智慧工地视频监管风险预警平台的应用

智慧工地方案是一种结合现代化技术与工地管理实践的创新型解决方案。它通过实时监控、数据分析、人工智能等技术手段&#xff0c;使工地管理更加高效、智能化。在建设智慧工地的过程中&#xff0c;除了上述提到的利用物联网技术实现设备互联、数据采集及分析以外&#xff0c;还…

jmeter CSV 数据文件设置

创建一个CSV数据文件&#xff1a;使用任何文本编辑器创建一个CSV文件&#xff0c;将测试数据按照逗号分隔的格式写入文件中。例如&#xff1a; room_id,arrival_date,depature_date,bussiness_date,order_status,order_child_room_id,guest_name,room_price 20032,2023-8-9 14:…

【JavaSE】详解final关键字

在Java中&#xff0c;final可以用来修饰类、方法和变量。final修饰类&#xff0c;表示该类无法被继承&#xff0c;并且此类的设计已被认为很完美而不需要进行修改或扩展。final修饰类中的方法&#xff0c;表示不可以被重写&#xff1b;也就是把该方法锁定了&#xff0c;以防止继…

PCL中的ISS特征点检测

ISS是基于内部形态描述子(ISS) 的特征点。 算法检测流程(参考论文:基于ISS 特征点结合改进ICP 的点云配准算法): PCL中的实现: template<typename PointInT, typename PointOutT, typename NormalT> void pcl::ISSKeypoint3D<PointInT, PointOutT, NormalT>…

将vue项目通过electron打包成windows可执行程序

将vue项目打包成windows可执行程序 1、准备好dist将整个项目打包 npm run build2、安装electron依赖 npm install electron --save-dev npm install electron-packager --save-dev"electron": "^13.1.4", "electron-packager": "^15.2.0…

Zabbix监控系统最新版安装

setenforce 0 设置SELinux 成为permissive模式 临时关闭selinux的 [rootwww yum.repos.d]# curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo % Total % Received % Xferd Average Speed Time Time Time Current …

Java“牵手”虾皮商品列表数据,关键词搜索虾皮(Shopee)商品数据接口,虾皮API申请指南

虾皮&#xff08;SHOPEE&#xff09;商城是一个网上批发购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取虾皮商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问虾皮商城的网页来获取商品详情…

C++入门:引用是什么

目录 1.引用的概念 2.引用的特征 3.常引用 4.引用使用场景 5.传值&#xff0c;传引用效率比较 6.引用与指针的区别 1.引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空 间&#xff0c;它和它引用…
最新文章