FixedThreadPool吞掉了异常
为了方便遍描述问题,如下是简化后的
public class RunException {
public static void main(String[] args) {
ExecutorService readerPool = Executors.newFixedThreadPool(3);
readerPool.submit(new Runnable() {
public void run() {
throw new RuntimeException("异常");
}
});
readerPool.shutdown();
}
}
此处FixedThreadPool吞掉了异常。
问题
- 为什么不能抛出到外部线程捕获
- submit为什么不能打印报错信息
- execute怎么使用logger打印报错信息
为什么不能抛出到外部线程捕获
jvm会在线程即将死掉的时候捕获所有未捕获的异常进行处理。默认使用的是Thread.defaultUncaughtExceptionHandler
submit为什么不能打印报错信息
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);//创建FutureTask类
execute(ftask);
return ftask;
}
查看FutureTask.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
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 interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
接着查看setException(ex);,将线程状态由completing改为exceptional,并将异常信息存在outcome中:
//这个方法就是这事线程状态为completing -> exceptional
//同时用outcome保存异常信息。
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
继续查看outcome的使用:
//report会抛出exception信息
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
//get会调用report()方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
- report会抛出exception信息,但report是私有方法;
- get会调用report()方法
所以如果需要获取异常信息就需要调用get()方法。
execute怎么输入logger日志
查看execute的实现ThreadPoolExecutor.execute():
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);
}
从代码可知,线程池将任务加入了任务队列,需要看看线程在哪执行任务的。那么只需要看看有没有获取任务的函数,ThreadPoolExecutor.getTask()即是获取任务的函数,通过查找,ThreadPoolExecutor.runWorker调用了ThreadPoolExecutor.getTask(),它应该是执行任务的代码:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean 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 interrupt
if ((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) {
//这里直接抛出所有Runtime异常
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);
}
}
代码注释中看到获取RuntimeException的位置了。
这里抛出的异常在哪里处理呢? 接下来处理是交由jvm处理,从已经学习的知识中只知道jvm调用Thread.dispatchUncaughtException来处理所有未捕获的异常
/**
* Dispatch an uncaught exception to the handler. This method is
* intended to be called only by the JVM.
*/
private void dispatchUncaughtException(Throwable e) {
getUncaughtExceptionHandler().uncaughtException(this, e);
}
这里可以根据该方法注释解释,意思就是这个方法只用于JVM调用,处理线程未捕获的异常。 继续查看getUncaughtExceptionHandler()方法:
public interface UncaughtExceptionHandler {s
void uncaughtException(Thread t, Throwable e);
}
// 处理类
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
// 默认处理类
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
/**
* 设置默认的处理类,注意是静态方法,作用域为所有线程设置默认的处理类
**/
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(
new RuntimePermission("setDefaultUncaughtExceptionHandler")
);
}
defaultUncaughtExceptionHandler = eh;
}
//获取默认处理类
public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
return defaultUncaughtExceptionHandler;
}
//获取处理类,注意不是静态方法,只作用域该线程
//处理类为空使用ThreadGroup
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
//设置处理类
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
/**
* Dispatch an uncaught exception to the handler. This method is
* intended to be called only by the JVM.
*/
private void dispatchUncaughtException(Throwable e) {
//获取处理类型进行异常处理
getUncaughtExceptionHandler(www.mumingyue.cn).uncaughtException(this, e);
}
如果线程UncaughtExceptionHandler处理器为空则threadGroup处理器 查看threadGroup:
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t,www.douniu2.cc e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
从代码中可以看出,
- 如果父进程不为空,则使用父进程处理未捕获异常;
- 如果无父进程,则获取默认的
UncaughtExceptionHandler进行处理。- 默认的
UncaughtExceptionHandler为null,则使用Sytem.err将错误信息输出; - 默认的
UncaughtExceptionHandler不为null,则使用UncaughtExceptionHandler进行处理。
- 默认的
所以有两个方法实现用logger输出:
- Thread定义
uncaughtExceptionHandler:Thread.setUncaughtExceptionHandler(www.tianscpt.com),该方法仅能设置某个线程的默认UncaughtExceptionHandler。 - Thread定义
defaultUncaughtExceptionHandler:使用Thread.setDefaultUncaughtExceptionHandler,该方法设置所有线程的默认UncaughtExceptionHandler。
测试程序
仅某个线程设置默认UncaughtExceptionHandler
public static void oneThreadUncaughtExceptionHandler() {
Thread t1 = new Thread((www.mhylpt.com/) -> {
throw new RuntimeException(" t1 runtime exception");
}, "t1");
t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(Thread.currentThread(www.baihuiyulep.cn) + "trigger uncaugh exception handler");
}
});
t1.start();
Thread t2 = new Thread(() -> {
throw new RuntimeException(" t2 runtime exception");
}, "t2");
t2.start();
}
设置defaultUncaughtExceptionHandler
public static void defaultThreadUncaughtExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(www.ysyl157.com Thread t, Throwable e) {
System.out.println(Thread.currentThread() + "trigger uncaugh exception handler");
}
});
new Thread(() -> {
throw new RuntimeException(www.tianjiuyule178.com " t1 runtime exception");
}, "t1").start();
new Thread(() -> {
throw new RuntimeException(" t2 runtime exception");
}, "t2").start();
}
解惑
那为什么我们的例子代码中,异常不会输出呢?应该有兜底的
System.err来输出异常才对。 不是这样的,我们的例子中的异常实际上是处理了的,它捕获了异常,并且保存到了outcome中。仅仅有未捕获的异常,JVM才会调用Thread.dispatchUncaughtException来处理。
FixedThreadPool吞掉了异常的更多相关文章
- [Net 6 AspNetCore Bug] 解决返回IAsyncEnumerable<T>类型时抛出的OperationCanceledException会被AspNetCore 框架吞掉的Bug
记录一个我认为是Net6 Aspnetcore 框架的一个Bug Bug描述 在 Net6 的apsnecore项目中, 如果我们(满足以下所有条件) api的返回类型是IAsyncEnumerabl ...
- 一个问题:关于finally中return吞掉catch块中抛出的异常
今天遇到一个感觉很神奇的问题,记录一下问题以及自己分析问题的思路. 预警:不知道怎么看java字节码的朋友可能需要先看一下如何阅读java字节码才能看懂后面的解释. 我有一段程序: public cl ...
- 读书笔记 effective c++ Item 8 不要让异常(exceptions)离开析构函数
1.为什么c++不喜欢析构函数抛出异常 C++并没有禁止析构函数出现异常,但是它肯定不鼓励这么做.这是有原因的,考虑下面的代码: class Widget { public: ... ~Widget( ...
- 唯品会Java开发手册》1.0.2版阅读
<唯品会Java开发手册>1.0.2版阅读 1. 概述 <阿里巴巴Java开发手册>,是首个对外公布的企业级Java开发手册,对整个业界都有重要的意义. 我们结合唯品会的内部经 ...
- python基础-面向对象进阶
一.什么是反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被 ...
- Python之路【第六篇】python基础 之面向对象进阶
一 isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 和 issubclass(su ...
- 分享总结:更好地CodeReview
代码质量分享 2016_06_24_舒琴_代码质量.key For 代码提交人 基本原则 Review时机: 对于普通bugfix或优化,CodeReview最迟要 ...
- J2EE开发规范
J2EE开发规范一 JAVA编码规范1 命名规范1.1 包命名 包名称必须全部用小写. 命名方式:业务领域名.公司名.项目名.模块名 如com.yr.xxx.dao.1.2 类命名类名以英文单词取 ...
- Python系列之 - 上下文管理协议
with obj as f: '代码块' 1.with obj ---->触发obj.__enter__(),拿到返回值 2.as f----->f=返回值. 3.with obj as ...
随机推荐
- es6 proxy代理
es6 新增构造函数 Proxy Proxy 构造函数,可以使用new 去创建,可以往里面插入两个参数,都是对象 let target = {} let handler = {} let proxy ...
- 图像的膨胀与腐蚀——OpenCV与C++的具体实现
目录 1. 膨胀与腐蚀的原理 2. 膨胀的具体实现 1) OpenCV实现 2) C/C++实现 3) 验证与结果 3. 腐蚀的具体实现 1. 膨胀与腐蚀的原理 膨胀与腐蚀是数学形态学在图像处理中最基 ...
- SSH实现登陆拦截器
/** * 登录验证拦截器 * */ @SuppressWarnings("serial") public class LoginInteceptor implements Int ...
- android开发中调用python代码(带参数)
android开发主要用到的是java代码,但是当开发涉及到一些算法时,往往用python可以提高软件的运行速度,也更加便捷,这里分享自己项目调用python代码的方式,主要有以下几个步骤(个人方法, ...
- Jmeter 接口测试实战-有趣的cookie
Jmeter 接口测试实战-有趣的cookie 场景: 接口测试时常都需要登录,请求方式(post), 登录常用的方法有通过获取token, 获取session, 获取cookie, 等等. 这几种都 ...
- d3js scales深入理解
转自:https://www.cnblogs.com/kidsitcn/p/7182274.html 比例尺函数是这样的javascript函数: 接收通常是数字,日期,类别等data输入并且: 返回 ...
- Python编写的Linux邮件发送工具
之前有用过Linux自带的mail工具来定时发送邮件,但是要装mailx还有配mail.rc,这还比较正常,关键是到了ubantu下这工具用起来真是操蛋,如果哪天其他的unix like操作系统也有需 ...
- Win7下emacs简单配置
;;win7下.emacs在C:\Users\用户名\AppData\Roaming目录下 在.emacs文件中添加 ;; cancel welcome page取消欢迎界面(setq inhibit ...
- SpringBoot使用qq邮箱发送邮件
最近公司要做一个邮箱注册和重置密码的功能,因为之前就做过,但是不是Springboot项目,所以相对来说还是比较容易的,在这里记录一下. 一.引用Maven依赖 这里使用spring自带的邮件jar包 ...
- 【心得】Lattice Diamond 后端约束实战小结
[博客导航] [导航]FPGA相关 IOB约束 参考<插入IO寄存器和位置约束---lattice&diamond>,推荐的方法是: 1.在strategy设置[Map Desig ...