线程池异常信息统一处理,从线程异常信息丢失说起

SAP生产订单(创建-下达-发料-报工-入库)全流程及反向流程 SAP创建下达报工入库发料以及取消发料取消入库,批量下达删除关闭,生产订单出入库查询和状态更改查询记录 阅读详情

异常信息作为我们平时定位问题的一大法宝,其重要性是不言而喻的。一段代码出现了问题,如果它的异常信息被吃了,那么一个本来很简单的问题,定位起来也会变的很麻烦。
今天我们就从异常信息被吃掉,导致相应信息没有输出到日志的问题说起。

一个简单的例子

首先我们看看如下的代码:

public class ThreadLogTestApplication {

    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(1,
                1, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        Runnable runnable = new Runnable() {
            public void run() {
                System.out.println("Do thread work!");
                throw new RuntimeException("Test Message!");
            }
        };
        pool.execute(runnable);
        pool.submit(runnable);
        pool.shutdown();
    }
}

这段代码非常的简单,创建一个线程池,实现一个Runnable,然后通过两种不同方式来执行这个它。代码很简单,那么它的运行结果是怎么样的呢?
下图就是这段代码的执行输出:
在这里插入图片描述
从结果可以发现,通过execute执行是可以正常输出出错堆栈的。而通过submit的方式执行异常信息不见了,这是为什么呢?

submit方法执行时异常信息为什么消失了

要想了解为什么submit执行的线程异常信息为什么消失了,就需要进一步分析它的源码:

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

从小段源码我们就可以知道submit和execute的区别了,submit会把参数中的Runnable通过RunnableFuture封装一层后再调用execute,所以很明显这个异常信息是被这个Future吃掉的了。

Future是如何吃掉异常信息的

进一步查看代码,我们可以知道submit方法创建的Future实际类型是FutureTask,下面就是它最为核心的run方法源码:

public void run() {
    // ...
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
      // ...
    }
}
protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion();
    }
}

从这段代码可以发现,线程执行时抛出的任何异常信息(catch的是Throwable,所以连OOM都会被catch掉)都会被catch并通过setException保存为执行结果。
所以submit提交的线程任务执行的异常信息是不会直接抛出的,而是作为结果保存在返回的Future中,当我们调用Future.get方法的时候异常就会被抛出。由于run方法返回值类型是void的,所以Future中保存的一般都是异常信息。

如何拿到submit提交的线程任务的异常信息

上面分析了submit提交会生成Future,并且异常信息是作为结果保存在Future中的,所以想要拿到submit提交的线程任务的异常信息也就非常的简单了。

public class ThreadLogTestApplication {

    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(1,
                1, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        Runnable runnable = new Runnable() {
            public void run() {
                System.out.println("Do thread work!");
                throw new RuntimeException("Test Message!");
            }
        };
        pool.execute(runnable);
        Future future = pool.submit(runnable);
        try {
            future.get();
        }
        catch (Exception e){
            e.printStackTrace();
        }
        pool.shutdown();
    }
}

通过上面的分析,我们已经清楚了submit方式和execute方式的异常信息的处理方法。但是还有一个问题:这两种提交方式的异常信息处理方式是不一样的,那么有没有办法实现统一呢?办法肯定是有的!

通过统一Runnable父类实现异常信息的统一处理

我们可以实现一个统一的Runnable父类,重写父类的run方法,定义一个running方法,然后run中通过try-catch包裹调用running方法,就能够抓取全部的异常信息,从而不向上抛异常,并且能够统一处理这些异常信息。实现代码如下:

public abstract class CustomRunnable implements Runnable{

    public final void run() {
        try{
            this.running();
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

    public abstract void running();
}

通过自定义的线程池来对异常信息进行统一的处理。

通过查看线程池run方法相关的源码我们可以发现,每次执行完线程任务后都会调用afterExecute方法,如下面的代码所示:

try {
    task.run();
} catch (RuntimeException x) {
    thrown = x; throw x;
} catch (Error x) {
    thrown = x; throw x;
} catch (Throwable x) {
    thrown = x; throw new Error(x);
} finally {
    afterExecute(task, thrown);
}

所以我们可以通过继承线程池类来重写这个afterExecute方法,从而达到异常信息的统一处理。实现如下所示:

@Override
protected void afterExecute(Runnable r, Throwable t) {
    super.afterExecute(r, t);
    if(t != null){
        t.printStackTrace();
    }
    if(r instanceof Future){
        Future f = (Future)r;
        try{
            f.get();
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}

扫码关注
头条号:  微信公众号:

128、点云配准与ICP:经典配准算法与深度学习配准 上周调试一台双臂协作机器人,左手持结构光相机扫描桌面上的随机堆叠工件,右手准备执行抓取。问题出在“对齐”这一步——相机扫描得到的点云和CAD模型点云之间差了大约15度的旋转,导致抓取姿态计算出来直接怼到桌面上。我第一反应是“ICP跑一下不就行了”,结果跑了三分钟,误差纹丝不动,甚至越迭代越发散。后来查了日志才发现,初始位姿给得太离谱,ICP直接掉进了局部最优。这个场景太典型了——很多人以为点云配准就是调个ICP库函数,实际工程里坑比想象中多得多。今天这篇笔记,咱们把点云配准这件事从头捋一遍。 阅读详情

相关推荐

AURIX-TC3xx-GTM详解六-DPLL(Digital PLL Module)-寄存器1-DPLL寄存器

主要有下图这些寄存器2、

m0_69929716的博客 440

ThreadPoolExecutor执行任务,异常日志缺失问题

之前在使用自定义线程池异步执行耗时任务时,一直记着如果业务方法抛出异常没有捕获,那么是看不到日志框架输出的异常日志的,所以总是在业务方法中包裹一层try-catch捕获可能发生的异常。也未去深入为什么没有打印异常日志和解决方法,这里记录下自己的总结。 1、事例 @Slf4j @SpringBootTest @RunWith(SpringRunner.class) publi...

alex920618的专栏 2413

NY8A053E 例程 之 PWM Ouput

更多关于NY8A053E的相关例程请留言!

倪彬笑阳的博客 2925

java多线程异常丢失

java多线程异常丢失

weigeshikebi的博客 3537

Ta-Lib源码解析(三):蜡烛图指标 (Candlestick Indicator) #(附Python重构代码和示意图)(补充中)

蜡烛图指标为识别K线形态的指标,笔者认为重点在于定义方式所提供的思路,背后的投资逻辑可以借鉴但不建议直接参考文档的翻译内容和源码逻辑存在不一致的现象,笔者分别列出中文百科中的定义和源码实际逻辑,供大家参考技术分析理论中的一些模糊词,ta-lib中给出了精确的定义源码

量化就是探索生活 3254

ADS实战指南:负载牵引(Loadpull)在射频功放设计中的关键应用

本文深入解析了负载牵引(Loadpull)技术在射频功率放大器设计中的核心作用与实战方法。通过生动的比喻和详细的ADS软件操作指南,阐述了如何通过系统扫描负载阻抗,在史密斯圆图上绘制等功率线与等效率线,从而精准找到最优阻抗点,以平衡输出功率与效率,是应对5G等现代通信系统严苛要求的必备技能。

weixin_29198045的博客 601

XXJ-JOB 执行器 在线程池内日志输出错乱的问题

问题:最近在使用 xxl-job 时,执行器使用异步线程处理任务时,发现日志输出存错乱。第一次的日志能够正常打印到对应的log文件中,后面每次都会打印到最开始的日志文件中如下分析XxlJobHelper 源码:XxlJobContext 源码 :XxlJobContext 使用InheritableThreadLocal, InheritableThreadLocal主要用于子线程创建时,需要自动继承父线程的ThreadLocal变量,方便必要信息的进一步传递。

zero_fxj的博客 2170

智能优化电池:基于单片机的电池管理系统

设计简介: 本设计是基于单片机的电池管理系统,主要实现以下功能: 可通过LCD1602显示温度、电压电流值; 可通过按键调整温度、电压和电流的阈值; 用ADC0832将模拟量转换为数字量。 标签:51单片机、LCD1602、ADC0832 效果图: 总体资料: 原理图: 软件设计流程: 系统框图: 本设计以STC89C52单片机为核心控制器,加上其他的模块一起组成电动汽车的电池管理的整个系统,...

专注单片机实战教学,分享单片机学习资料 375

多进程log4j日志丢失问题分析

转自:http://hellojavaer.iteye.com/blog/977599

lskyne的专栏 9741

YOLO26改进策略【损失函数篇】| AAAI 2025 SD Loss,基于目标尺度动态调整尺度损失和位置损失的影响系数,减少 IoU 标签波动

在红外小目标检测任务里,基于交并比的损失存在波动问题,像边界框(BBox)标签的IoU损失波动最高可达86%,较小目标的IoU损失稳定性更差,严重影响模型稳定性和回归效果。同时,现有损失函数没有充分考虑不同目标尺度下对尺度和位置的敏感度差异,,并且掩码标签的位置损失在处理漏检物体时难以收敛,容易产生误报。这些问题限制了模型在不同尺度目标上的检测性能,为解决这些问题,提出了损失函数。

Limiiiing的博客 396

深度解读 ABAP CDS View 中的 @Analytics.dataCategory: #FACT 注解

注解在 ABAP CDS View 中具有重要意义。它不仅定义了当前视图在数据模型中的角色,还为后续的分析和报告奠定了基础。在实际开发中,通过合理使用这一注解,可以显著提升数据模型的可用性和系统性能。无论是简单的 KPI 展示还是复杂的多维分析,这一注解都能发挥关键作用。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 788

Java线程池执行的任务抛出异常看不到日志

问题描述:  最近项目中一些异步执行的逻辑没有运行异常却没有打出日志 给定位问题带来麻烦?? 问题分析 接下来我们来看一下java中的线程池是如何运行我们提交的任务的,详细流程比较复杂,这里我们不关注,我们只关注任务执行的部分。java中的线程池用的是ThreadPoolExecutor,真正执行代码的部分是runWorker方法:final void runWork

taotoxht的专栏 1万+

【数据分享】12.5米分辨率DEM地形数据(分省)

该数据来源于NASA地球科学数据网站(https://nasadaacs.eos.nasa.gov/),是ALOS卫星获取的高精度地形数据。该数据由于精度高,包括全省范围的数据和省内每个城市范围的数据!

JData_Engineer的博客 2171

多线程某个线程异常了怎么处理_线程池异常信息统一处理,从线程异常信息丢失说起...

异常信息作为我们平时定位问题的一大法宝,其重要性是不言而喻的。一段代码出现了问题,如果它的异常信息被吃了,那么一个本来很简单的问题,定位起来也会变的很麻烦。今天我们就从异常信息被吃掉,导致相应信息没有输出到日志的问题说起。一个简单的例子首先我们看看如下的代码:这段代码非常的简单,创建一个线程池,实现一个Runnable,然后通过两种不同方式来执行这个它。代码很简单,那么它的运行结果是怎么样的呢?下...

weixin_39927144的博客 1310

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

记一次线程池引发的BUG,差点被祭天

吓尿了

敖丙 1万+

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

java - 统一管理项目的线程池

为什么需要统一管理线程池呢?因为频繁地自建线程会浪费CPU资源,可能导致OOM问题。另外,频繁地创建线程,销毁线程会给系统带来额外的开销。随着访问量的增加,系统会有崩溃的可能。因此,通常我们需要统一管理项目的线程池。

m0_73478445的博客 1300

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

线程池发生了异常该怎么处理?

为自动重试机制添加退避策略(Exponential Backoff)。任务抛出未捕获异常 → 导致工作线程死亡!线程池会创建新线程替代。异常信息丢失(仅打印到控制台,生产环境不可见)。通过JMX暴露指标(需继承。显式try-catch捕获。Future.get捕获。会导致异常永久丢失!

牛马baby的博客 994

Java线程池异常处理机制

线程池技术是服务器端开发中常用的技术。不论是直接还是间接,各种服务器端功能的执行总是离不开线程池的调度。关于线程池的各种文章,多数是关注任务的创建和执行方面,对于异常处理和任务取消(包括线程池关闭)关注的偏少。接下来,本文将从 Java 原生线程、两种主要线程池和这三方面介绍 Java 中线程的异常处理机制。

LiZhen314的博客 887

使用线程池你应该知道的知识点

多线程编程是每一个开发必知必会的技能,在实际项目中,为了避免频繁创建和销毁线程,我们通常使用池化的思想,用线程池进行多线程开发。线程池在开发中使用频率非常高,也包含不少知识点,是一个高频面试题,本篇总结线程池的使用经验和需要注意的问题,更好的应对日常开发和面试。如有更多知识点,欢迎补充~

dsgdauigfs的博客 1480

Java线程池最佳实践

引言 在日常的开发工作当中,线程池往往承载着一个应用中最重要的业务逻辑,因此我们有必要更多地去关注线程池的执行情况,包括异常的处理和分析等等。本文主要聚焦在如何正确使用线程池上,以及提供一些实用的建议。文中会稍微涉及到一些线程池实现原理方面的知识,但是不会做过多展开。网络上关于线程池的原理以及源码解析的文章有很多,感兴趣的同学可以自行查阅。 (想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更...

wanghao112956的博客 2052

线程池原理

线程池是一种池化技术,用于预先创建并管理一组线程,避免频繁创建和销毁线程的开销,提高性能和响应速度。核心线程数最大线程数空闲存活时间单位时间任务队列线程工厂和拒绝策略。corePoolSize:核心线程数,即线程池中始终保持的线程数量。maximumPoolSize:最大线程数,即线程池中允许的最大线程数量。keepAliveTime:线程空闲时间,超过这个时间的非核心线程会被销毁。workQueue:任务队列,存放待执行的任务。hreadFactory:线程工厂,用于创建新线程。

m0_61057201的博客 3415

java 非法线程_Java 并发之线程池未捕获异常信息丢失的问题

在使用 JUC 的线程池时(ThreadPoolExecutor 等),需要注意执行任务时可能会抛出的各种 RuntimeException。如果直接使用 JDK 提供的线程池实现的时候,很有可能发生任务“悄无声息”、“莫名其妙”地就结束了。其实,这种情况的发生是因为你没用捕获并处理 Runnable 或 Callable 中发生的异常。在你写命令行程序的时候,上述情况并不会发生。那是因为线程的默...

weixin_29032337的博客 376

java多线程(6):线程与线程池的异常处理机制

前言 java提供了一套完善的异常处理机制,异常的基础本文不展开,本文重点讲述线程的异常处理。 正文 本文将线程的实现分为2种,一种是实现了Runnable接口的无返回值的线程,无法在主线程感知到子线程的异常,没有被捕获的异常只会输出到控制台,那么未被捕获的异常如果没有进行处理或者日志记录,就会造成异常信息丢失;另一种是实现了Callable接口的有返回值的线程,子线程的异常信息会通过FutureTask返回值传递给主线程。最后再说一下线程池的异常处理方式。 一、线程的异常处理方法 1、主线程无法捕获子线程

欢迎来到Jimmy的博客 3532

【线程池正确处理异常的三种方式】

🧩线程池异常不外抛,try…catch要记牢;Callable + Future能回报,Handler兜底最可靠。

2501_93101910的博客 415

【转载】Java线程池异常处理机制

多线程异常处理

代码的深渊 1016

多线程(线程池)异常丢失

如下代码,ThreedTask实现了Runnable方法,我们在run方法中,进行了一个判断,如果当前ThreedTask的taskReq中的任务参数为3,则我们模拟一个除0错误。然后看日志,不难发现,出现了预期的除0错误。这里我们捕获异常,或者抛出异常。如果我们自己主动在run方法中自己加上try catch,运行以后,就能看到异常信息了。以下代码,在线程池里面提交了5个线程任务,不出意外,当任务参数为3的时候,会报错。这时候我们把上面的多线程代码改动下,变成普通的threedTask.run方法。

歪桃的博客 2609
上一篇: LeetCode算法题:008-字符串转换整数 (atoi)
下一篇: Java内存泄漏!为什么会泄漏?如何泄漏?怎么定位?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值