线程池吞掉异常的case:源码阅读与解决方法

1. 问题背景

有一天给同事CR,看到一段这样的代码

try {for (param : params) {//并发处理,func无返回值ThreadPool.submit(func(param));}
} catch (Exception e) {log.info("func抛异常啦,参数是:{}", param)
}

我:你这段代码是利用并发降低RT对吧,如果func内部抛异常,你确定可以catch到吗

同事:可以啊, 为什么不可以(...

我:不如你run一把,在func mock一个异常出来试试

同事:我靠还真是

我:你可以用execute,改动比较小

同事:那么是为什么呢

2. 同事的例子

import java.util.concurrent.*;public class ThreadPoolTest {public static void main(String[] args) throws Exception {ExecutorService executorService = new ThreadPoolExecutor(10, 20, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>());testExecute(executorService);Thread.sleep(2000);testSubmit1(executorService);Thread.sleep(2000);testSubmit2(executorService);}private static void testExecute(ExecutorService executorService) {executorService.execute(() -> {System.out.println("执行线程池execute方法");throw new RuntimeException("execute方法抛出异常");});}private static void testSubmit1(ExecutorService executorService) {executorService.submit(() -> {System.out.println("执行线程池submit方法1");throw new RuntimeException("submit方法1抛出异常");});}private static void testSubmit2(ExecutorService executorService) throws Exception {Future<Object> feature = executorService.submit(() -> {System.out.println("执行线程池submit方法2");throw new RuntimeException("submit方法2抛出异常");});feature.get();}
}

执行结果:

执行线程池execute方法
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: execute方法抛出异常at ThreadPoolTest.lambda$testExecute$0(ThreadPoolTest.java:23)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)
执行线程池submit方法1
执行线程池submit方法2
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException: submit方法2抛出异常at java.util.concurrent.FutureTask.report(FutureTask.java:122)at java.util.concurrent.FutureTask.get(FutureTask.java:192)at ThreadPoolTest.testSubmit2(ThreadPoolTest.java:39)at ThreadPoolTest.main(ThreadPoolTest.java:17)
Caused by: java.lang.RuntimeException: submit方法2抛出异常at ThreadPoolTest.lambda$testSubmit2$2(ThreadPoolTest.java:37)at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)at java.util.concurrent.FutureTask.run(FutureTask.java)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)

3. 原理分析

3.1 线程池包的继承结构

3.2 submit和execute方法的差异

3.2.1 execute

方法定义在最顶层的Executor接口,并且Executor接口有且仅有这一个方法

public interface Executor {/*** Executes the given command at some time in the future.  The command* may execute in a new thread, in a pooled thread, or in the calling* thread, at the discretion of the {@code Executor} implementation.** @param command the runnable task* @throws RejectedExecutionException if this task cannot be* accepted for execution* @throws NullPointerException if command is null*/void execute(Runnable command);
}

方法实现在ThreadPoolExecutor:

    public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command);}

实际执行的过程,在worker(是runnable的实现类)的run方法,run方法实际执行的是runWorker方法

    final void runWorker(Worker w) {Thread wt = Thread.currentThread();Runnable task = w.firstTask;w.firstTask = null;w.unlock(); // allow interruptsboolean completedAbruptly = true;try {while (task != null || (task = getTask()) != null) {w.lock();// If pool is stopping, ensure thread is interrupted;// if not, ensure thread is not interrupted.  This// requires a recheck in second case to deal with// shutdownNow race while clearing interruptif ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())wt.interrupt();try {beforeExecute(wt, task);Throwable thrown = null;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);}} finally {task = null;w.completedTasks++;w.unlock();}}completedAbruptly = false;} finally {processWorkerExit(w, completedAbruptly);}}

可以看到执行过程中,如果task.run();发生异常,没有catch处理,异常会层层向外抛出;最终进入finally块,执行processWorkerExit;

3.2.2 submit

submit方法定义在ExecutorService

public interface ExecutorService extends Executor {/*** Submits a value-returning task for execution and returns a* Future representing the pending results of the task. The* Future's {@code get} method will return the task's result upon* successful completion.** <p>* If you would like to immediately block waiting* for a task, you can use constructions of the form* {@code result = exec.submit(aCallable).get();}** <p>Note: The {@link Executors} class includes a set of methods* that can convert some other common closure-like objects,* for example, {@link java.security.PrivilegedAction} to* {@link Callable} form so they can be submitted.** @param task the task to submit* @param <T> the type of the task's result* @return a Future representing pending completion of the task* @throws RejectedExecutionException if the task cannot be*         scheduled for execution* @throws NullPointerException if the task is null*/<T> Future<T> submit(Callable<T> task);/*** Submits a Runnable task for execution and returns a Future* representing that task. The Future's {@code get} method will* return the given result upon successful completion.** @param task the task to submit* @param result the result to return* @param <T> the type of the result* @return a Future representing pending completion of the task* @throws RejectedExecutionException if the task cannot be*         scheduled for execution* @throws NullPointerException if the task is null*/<T> Future<T> submit(Runnable task, T result);/*** Submits a Runnable task for execution and returns a Future* representing that task. The Future's {@code get} method will* return {@code null} upon <em>successful</em> completion.** @param task the task to submit* @return a Future representing pending completion of the task* @throws RejectedExecutionException if the task cannot be*         scheduled for execution* @throws NullPointerException if the task is null*/Future<?> submit(Runnable task);
}

实现在AbstractExecutorService

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

可以看到这里创建了RunnableFuture(而不是基础的worker),顾名思义,RunnableFuture同时实现了Runnable和Future接口,也就意味着可以对该任务执行get操作,看看RunnableFuture的run方法:

    public void run() {if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return;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 {// runner must be non-null until state is settled to// prevent concurrent calls to run()runner = null;// state must be re-read after nulling runner to prevent// leaked interruptsint s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}}

catch块对方法异常做了处理,与执行结果一同在Future中暂存起来;submit()执行完毕后返回Future对象,执行future.get()会触发异常的抛出;

当然了,如果你只是执行了submit,没有获取future,异常就会“神奇地”消失。

参考:

Java线程池实现原理及其在美团业务中的实践 - 美团技术团队

https://zhuanlan.zhihu.com/p/651997713

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

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

相关文章

六西格玛助力便携式产品功耗大降:打造绿色节能新标杆!

随着功能的日益强大&#xff0c;便携式电子产品的功耗问题也日益凸显&#xff0c;成为制约产品性能提升和用户体验改善的关键因素。为了应对这一挑战&#xff0c;越来越多的企业开始探索应用六西格玛方法来降低便携式产品的功耗&#xff0c;实现绿色节能的目标。 六西格玛是一…

Enhancing CLIP with GPT-4: Harnessing Visual Descriptions as Prompts

标题&#xff1a;用GPT-4增强CLIP:利用视觉描述作为提示 源文链接&#xff1a;Maniparambil_Enhancing_CLIP_with_GPT-4_Harnessing_Visual_Descriptions_as_Prompts_ICCVW_2023_paper.pdf (thecvf.com)https://openaccess.thecvf.com/content/ICCV2023W/MMFM/papers/Manipara…

FPGA - 滤波器 - IIR滤波器设计

一&#xff0c;IIR滤波器 在FPGA - 滤波器 - FIR滤波器设计中可知&#xff0c;数字滤波器是一个时域离散系统。任何一个时域离散系统都可以用一个N阶差分方程来表示&#xff0c;即&#xff1a; 式中&#xff0c;x(n)和y(n)分别是系统的输入序列和输出序列&#xff1b;aj和bi均为…

大腾智能正式入驻华为云

5月30日&#xff0c;大腾智能正式入驻华为云云商店。作为一家基于云原生的国产工业软件与数字化协同平台&#xff0c;大腾智能专注于推动企业数字化转型与升级&#xff0c;为企业提供一系列专业、高效的云原生数字化软件及方案。 华为云云商店&#xff0c;作为业界标杆&#xf…

【Win】识别Hyper-V虚拟机第一代与第二代及其差异

Hyper-V作为微软强大的虚拟化平台&#xff0c;允许用户创建虚拟机并安装各种操作系统。但您是否知道Hyper-V虚拟机分为第一代和第二代&#xff0c;并且它们之间存在一些关键差异&#xff1f;本文将指导您如何识别您的虚拟机属于哪一代&#xff0c;并详细解释两者之间的主要区别…

小白Linux提权

1.脏牛提权 原因&#xff1a; 内存子系统处理写入复制时&#xff0c;发生内存条件竞争&#xff0c;任务执行顺序异常&#xff0c;可导致应用崩溃&#xff0c;进一步执行其他代码。get_user_page内核函数在处理Copy-on-Write(以下使用COW表示)的过程中&#xff0c;可能产出竞态…

Avalonia for VSCode

1、在VSCode中编辑AvaloniaUI界面&#xff0c;在VSCode中搜索Avalonia&#xff0c;并安装。如下图&#xff0c;可以发现Avalonia for VSCode还是预览版。 2、 创建一个Avalonia 项目。 选择项目类型 输入项目名称 选择项目所在文件夹 打开项目 3、项目架构如下图。 4、builde…

ICBINP - “I Can‘t Believe It‘s Not Photography“

ICBINP - “I Can’t Believe It’s Not Photography” 推荐设置&#xff1a; DPM 3M SDE Karras or DPM 2M Karras, 20-30 steps, 2.5-5 CFG (or use Dynamic Thresholding), happiest at 640x960 with a hires fix on top, but if you are happy to hunt through seeds to a…

汇聚荣拼多多运营策略是怎么样的?

拼多多作为中国领先的电商平台&#xff0c;其运营策略一直备受关注。拼多多的成功不仅在于其创新的“社交电商”模式&#xff0c;更在于其精细化的市场定位和高效的用户增长策略。本文将深入分析拼多多的运营策略&#xff0c;探讨其如何在激烈的电商竞争中突围而出。 一、用户增…

基于Java的度分秒坐标转纯经纬度坐标的漂亮国基地信息管理

目录 前言 一、空间表设计 1、物理表结构 二、后台数据管理 1、数据去重 2、去重的具体实现 3、度分秒数据格式转换 4、具体的转换方法 5、新增界面的实现 三、数据管理界面 总结 前言 众所周知&#xff0c;漂亮国在全球范围内部署了大量的基地&#xff0c;用以维持其…

Unity OpenCVForUnity 安装和第一个案例详解 <一>

目录 一、资源简介 二、安装使用 1.下载案例Demo 2.移动StreamingAssets文件夹 3.添加场景 三、今日案例 1.案例Texture2DToMat Example 2.什么是Mat&#xff1f; 3.如何把Texture2D变成Mat &#xff08;1&#xff09;.初始化Mat &#xff08;2&#xff09;.Cv_…

Bagging与Boosting的应用与优势

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

【最全面最优质的PyTorch学习资源】

纯 PyTorch 资源 PyTorch 博客 https://pytorch.org/blog/ PyTorch 文档 https://pytorch.org/docs PyTorch 性能调优指南 https://pytorch.org/tutorials/recipes/recipes/tuning_guide.html# PyTorch Recipes https://pytorch.org/tutorials/recipes/recipes_index.h…

项目管理进阶之EVM(挣值管理)

前言 项目管理进阶系列&#xff0c;终于有时间更新啦&#xff01;&#xff01;&#xff01;欢迎持续关注哦~ 上一节博主重点讲了一个环&#xff1a;PDCA&#xff0c;无论各行各业&#xff0c;上到航空航天、下到种地种菜&#xff0c;都离不开对质量的监督和改进。这个环既是一…

MyBatis 关于查询语句上配置的详细内容

1. MyBatis 关于查询语句上配置的详细内容 文章目录 1. MyBatis 关于查询语句上配置的详细内容2. 准备工作3. SQL查询结果&#xff0c;返回为POJO实体类型4. SQL查询结果&#xff0c;返回为List<POJO\> 集合类型5. SQL查询结果&#xff0c;返回为Map 集合6. SQL查询结果&…

洗地机性价比高的是哪一款?行内人告诉你

在浏览前&#xff0c;希望您轻触屏幕上方的“关注”按钮&#xff0c;让我后续为您带来更多实用且精彩的内容&#xff0c;感谢您的支持&#xff01; 洗地机作为现在的流行清洁工具&#xff0c;它的魅力之处在于&#xff1a;性价比极高&#xff0c;大多数家庭无需花费过多就能把…

IPNV6写法

黄色---一致 红色---取消 V4中的第二列用于分片 V6可以使用扩展首部实现 蓝色--替代 1、服务类型--扩展表 2、报头长度---有效负载长度 3、TTL--跳数限制 4、协议号---下一个头…

英伟达开源 3400 亿参数模型;苹果 iOS 18 紧急 SOS 新增实时视频功能丨 RTE 开发者日报 Vol.225

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

力扣172. 阶乘后的零

Problem: 172. 阶乘后的零 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.要使得末尾出现0&#xff0c;则乘式中必须出现因子2与5&#xff1b; 2.而由于对于一个数的阶乘&#xff0c;易知因子2的个数是大于因子5的个数&#xff08;因为只要出现偶数则可以分解出…

vscode c++ 开发环境配置

今天各位同学已经安装了mingw环境&#xff0c;但部分同学vscode开发环境又问题&#xff0c;究其原因&#xff0c;还是vscode 编译环境配置错误&#xff0c;有问题的同学 按如下步骤处理&#xff1a; 1、卸载相关插件&#xff0c;特别是中文插件&#xff0c;原因是暂时回避中文…