前言
之前在用线程池的时候,对于异常的处理一直搞不明白,又时候有异常输出,有时候没有,今天做了个总结。
实践
ExecutorService exec1 = Executors.newFixedThreadPool(1);
exec1.submit(()->{
Object obj = null;
System.out.println(obj.toString());
});
//上面方法无法输出异常,异常被吞掉了
exec1.submit(()->{
try {
Object obj = null;
System.out.println(obj.toString());
} catch (Exception e) {
e.printStackTrace();
}
});
// 加上try catch 之后可以输出异常
java.lang.NullPointerException
at com.alvinlkk.threadpool.ExecutorTest.lambda$main$1(ExecutorTest.java:24)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
exec1.execute(()->{
Object obj = null;
System.out.println(obj.toString());
});
//可以输出异常
Exception in thread "pool-1-thread-1" java.lang.NullPointerException
at com.alvinlkk.threadpool.ExecutorTest.lambda$main$1(ExecutorTest.java:23)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
ThreadPoolExecutor executor3 = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future>) {
try {
//get这里会首先检查任务的状态,然后将上面的异常包装成ExecutionException
Object result = ((Future>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null){
//异常处理
t.printStackTrace();
}
}
};
executor3.execute(()->{
Object obj = null;
System.out.println(obj.toString());
});
//可以输出异常
Exception in thread "pool-2-thread-1" java.lang.NullPointerException
at com.alvinlkk.threadpool.ExecutorTest.lambda$main$3(ExecutorTest.java:73)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
小结
线程池使用execute()方法会输出异常,只是使用submit()方法,不做其他处理,异常会被吞掉。具体线程池使用execute和submit有啥区别,再说。反正能用execute就不要用submit啦。
直接用try catch 捕获异常,个人觉得比较方便,比较常用。
创建线程池的时候,重写protected void afterExecute(Runnable r, Throwable t) { }方法。
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future>) {
try {
//get这里会首先检查任务的状态,然后将上面的异常包装成ExecutionException
Object result = ((Future>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null){
//异常处理
t.printStackTrace();
}
}
原理
为什么用submit异常会被吞掉,而execute不会?
public Future> submit(Runnable task) {
if (task == null) throw new NullPointerException();
//会对传入的task做个封装称为futureTask,然后同样调用execute
RunnableFuture ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
/**
* 代码还是很清爽的,一个很典型的生产者/消费者模型,
* 这里暂不纠结这些细节,那么如果提交到workQueue成功的话,消费者是谁呢?
* 明显在这个newWorker里搞的鬼,同样细节有兴趣可以自己再去研究,这里我们会发现
* 核心就是Worker这个内部类
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) //execute方法中会将任务添加到work中
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);
}
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 {
//调用任务的run方法
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);
}
}
submit中调用的run方法和execute的不一样,submit是FutureTask类的run,而execute是runnable的run方法。
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable 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 {
}
}
参考