为什么java线程池的submit的不抛出异常
前言
大家好,这里是经典鸡翅,最近有人问我线程池的execute和submit,有的抛出异常,有的不抛出异常,这里鸡翅老哥给大家整理下。通过源码跟踪跟大家讲解。
两个方法
我们先用最普通的方式定义一个线程池。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024), new ThreadPoolExecutor.AbortPolicy());
线程池有两种执行任务的方式,一种是execute方法,一种是submit方法。
我们引入一个最简单的例子。执行一个有异常的方法。
-
@Test
-
public void test() throws ExecutionException, InterruptedException {
-
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024), new ThreadPoolExecutor.AbortPolicy());
-
threadPoolExecutor.execute(() -> {
-
testThread();
-
});
-
}
-
-
private int testThread() {
-
int i = 1 / 0;
-
return i;
-
}
如上的一段代码,由于1/0的存在,必定是会出现异常的,我们执行后,看到结果,是有异常的。符合我们的预期。
-
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
-
at com.hq.hqframe.textUtil.TextUtilTest.testThread(TextUtilTest.java:104)
-
at com.hq.hqframe.textUtil.TextUtilTest.lambda$test$5(TextUtilTest.java:99)
-
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提交任务呢?
-
@Test
-
public void test() throws ExecutionException, InterruptedException {
-
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024), new ThreadPoolExecutor.AbortPolicy());
-
threadPoolExecutor.submit(() -> {
-
testThread();
-
});
-
}
-
-
private int testThread() {
-
int i = 1 / 0;
-
return i;
-
}
执行代码我们发现,控制台并没有打印出任何异常。异常被吞了。这就是execute和submit的第一个区别。
为什么submit不会抛出异常呢?
我们开始跟踪一下,为什么submit不会抛出异常。先看submit的第一个方法。
-
public Future<?> submit(Runnable task) {
-
if (task == null) throw new NullPointerException();
-
RunnableFuture<Void> ftask = newTaskFor(task, null);
-
execute(ftask);
-
return ftask;
-
}
将task进行包装为FutureTask。这里面的task是runnable的,我们将其包装成callable形式的。通过runnableadapter。
-
static final class RunnableAdapter<T> implements Callable<T> {
-
final Runnable task;
-
final T result;
-
RunnableAdapter(Runnable task, T result) {
-
this.task = task;
-
this.result = result;
-
}
-
public T call() {
-
task.run();
-
return result;
-
}
-
}
包装后,执行这个task。
-
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);
-
}
我们直接将目光聚焦到addWorker,这里面干了一个什么事呢?重点建造了一个Worker类。把当前的task传入进去。Worker类里面把自己的task作为一个线程赋值。
-
Worker(Runnable firstTask) {
-
setState(-1); // inhibit interrupts until runWorker
-
this.firstTask = firstTask;
-
this.thread = getThreadFactory().newThread(this);
-
}
回到addWorker处,我们可以看到start方法,这个方法,实际调用的就是线程的run方法。也就是我们worker类里面自己实现的run方法。
-
public void run() {
-
runWorker(this);
-
}
再看下runworker方法,我们发现里面继续调用了task.run方法。
-
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) {
-
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);
-
}
-
}
我们就可以重新追踪到futureTask。
-
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 interrupts
-
int s = state;
-
if (s >= INTERRUPTING)
-
handlePossibleCancellationInterrupt(s);
-
}
此时我们发现,异常已经被捕获了,并且调用了setexception方法。把我们的异常信息给了outcome。
-
protected void setException(Throwable t) {
-
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
-
outcome = t;
-
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
-
finishCompletion();
-
}
-
}
到此我们就明白了为什么submit没有抛出异常了!
文章来源: blog.csdn.net,作者:经典鸡翅,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/hanqing456/article/details/122293430
- 点赞
- 收藏
- 关注作者
评论(0)