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 ...
随机推荐
- 第一册:Lesson 123.
原文:A trip to Australia. question:Who is the man with the beard? Look ,Scott.This is a photograph I t ...
- c# String ,String[] 和 List<String>之间的转换
C#对字符串进行处理时,经常需要进行String,String[]和List<String>之间的转换 本文分析一下它们的差异和转换 一. 1. String > String[] ...
- Saslauthd服务实现SMTP发信认证
一.SMTP发信认证 通过sasl库中的saslauthd服务实现SMTP认证 二.部署 1.先安装postifx 略 2.生成sasl配置文件,实现使用sasl认证 vim /usr/lib64/s ...
- Django之无名分组,有名分组
在Django 2.0版本之前,在urls,py文件中,用url设定视图函数 urlpatterns = [ url(r'login/',views.login), ] 其中第一个参数是正则匹配,如下 ...
- [C#学习笔记3]关于Main(string[ ] args)中args命令行参数
Main(string[] args)方法是C#程序的入口,程序从这里开始执行,在这里结束.C#代码逻辑要包含在一个类型(Type)中,游离的.全局的变量或函数是不存在的,这里的类型包括类(class ...
- [20190418]exclusive latch spin count.txt
[20190418]exclusive latch spin count.txt--//昨天测试"process allocation" latch,主要这个latch与其它拴锁s ...
- Docker-Docker-compose应用
Docker-compose是用来定义和运行多容器应用的工具,它是独立于docker存在的,需要单独安装.实际应用场景中,我们的应用可能被打包运行在不同的容器里面,例如一个常规的web应用可能会涉及到 ...
- REST教程
REST教程 越来越多的人开始意识到,网站即软件,而且是一种新型的软件.这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high late ...
- Asp.Net WebAPI配置接口返回数据类型为Json格式
Asp.Net WebAPI配置接口返回数据类型为Json格式 一.默认情况下WebApi 对于没有指定请求数据类型类型的请求,返回数据类型为Xml格式 例如:从浏览器直接输入地址,或者默认的XM ...
- Windows 10 远程连接出现函数错误 【这可能由于CredSSP加密Oracle修正】
win+R 弹出窗口输入gpedit.msc 计算机配置=>管理模板=>系统=>凭据分配=>加密Oracle修正 编辑策略设置=>已启用=>保护级别=>易受攻 ...