ExecutorService的javadoc有时是指线程"由于失败"而终止的情况。 但是,尚不清楚这是指哪种故障。
例如,单线程执行程序文档说
if this single thread terminates due to a failure during execution
prior to shutdown, a new one will take its place if needed to execute
subsequent tasks
我本以为在Exception或RuntimeException的情况下可能会发生这种情况,但事实并非如此。 运行以下代码似乎在提供相同的线程名称和线程ID。
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
System.out.println("Hello from" + Thread.currentThread().getName()+"" + Thread.currentThread().getId());
throw new NullPointerException("Test");
});
executor.submit(() -> {
System.out.println("Hello 2 from" + Thread.currentThread().getName() +"" + Thread.currentThread().getId());
});
此代码的输出是:
Hello from pool-1-thread-1 12
Hello 2 from pool-1-thread-1 12
似乎即使在NullPointerException情况下,同一线程也正在被重用。
那么Javadoc指的是哪种"故障"?
这不是知识,而是猜测,但是failure对我来说,它们的含义与软件无关。 也许这意味着硬件故障?
嗯...与硬件相关的故障听起来太过激烈了...在那种情况下为什么要启动另一个线程,好像什么也没发生? 我想与OS相关的线程终止可能是更合理的情况。
这是个有趣的问题。在ThreadPoolExecutor中的代码之后,将Runnable传递给execute()方法时,该线程将被丢弃。
当您调用submit()时,执行程序为FutureTask类型的可调用/可运行对象创建一个包装器。 FutureTask.run()具有一些捕获异常并将其存储的逻辑(因此,您可以从Future查询此异常)。在这种情况下,异常永远不会到达ThreadPool,因此不会丢弃线程。
那么,什么将导致线程失败,ExecutorService检测到它的失败并产生新的线程呢?
由于包装程序,在调用submit()时不会发生这种情况。 如果调用execute()且线程引发异常,则线程执行程序将生成一个新线程。 要对此进行测试,请使用您的代码并将submit()替换为execute(),您将看到执行程序创建了一个新线程。
奥古斯托是正确的。当Runnable任务作为execute()方法中的参数传递时,在遇到异常后本应丢弃该线程。
我在本文和"未来任务"源代码中找到了有关"未来任务吞下异常"的具体证据。
**Inside FutureTask$Sync**
void innerRun() {
if (!compareAndSetState(READY, RUNNING))
return;
runner = Thread.currentThread();
if (getState() == RUNNING) { // recheck after setting thread
V result;
try {
result = callable.call();
} catch (Throwable ex) {
setException(ex);
return;
}
set(result);
} else {
releaseShared(0); // cancel
}
}
protected void setException(Throwable t) {
sync.innerSetException(t);
}
SE中围绕此主题还有一些其他有趣的问题。
从Java ExecutorService捕获线程异常
在ExecutorServices提交和ExecutorServices执行之间选择
编辑:
当线程代码中未捕获异常时,将发生线程故障或终止。如果您通过execute()而不是submit()提交任务,则除非您捕获到异常,否则不会捕获异常。线程代码未捕获到的异常将导致线程终止或失败,并且Executor将创建新线程。
如果通过submit(),提交任务,则会创建FutureTask,并且该任务将吞下代码中未捕获的异常。由于该异常是在FutureTask中捕获的,因此该线程不会被丢弃。
感谢您的回复。 我仍然不清楚它们指的是什么失败,这可能导致线程被丢弃并启动新线程。 您指示的代码清楚地表明已检查线程的状态。 我对导致线程死亡的原因更感兴趣(如果不是异常,无论如何都会被捕获)。
将来的异常会被吞下,但是使用execute()而不是Submit()提交的Runnable则不会。 如果遇到未捕获的异常,它们将死亡,并导致线程被丢弃。 更改代码以执行()并检查线程ID