转载自:https://crossoverjie.top/2019/03/12/troubleshoot/thread-gone/
现象:线程池使用时,任务中抛出一个没有捕获的异常时,线程池没有挂掉,并且任务也没有继续运行。
代码模拟:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
for (int i = 0; i <= 5; i++) {
if (i == 3) {
System.out.println(i / 0);
}
else if (i == 5) {
System.out.println("over");
}
else {
System.out.println(Thread.currentThread().getName() + " running...");
}
}
});
运行结果:
pool-1-thread-1 running...
pool-1-thread-1 running...
pool-1-thread-1 running...
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at org.zero.threadpool.PoolExceptionTest.lambda$demo0$0(PoolExceptionTest.java:23)
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)
线程抛出了异常,但是jstack发现还有一个"pool-1-thread-2" 线程处于waiting状态:
"pool-1-thread-2" #13 prio=5 os_prio=31 tid=0x00007fa509827000 nid=0xa903 waiting on condition [0x000070000ac40000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076adcc320> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
分析:
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);
}
}
具体的任务是通过getTask()从workQueue取出来的,然后执行task.run(),当抛出异常时,可以发现runWorker的确是把这个异常抛了出去,但是抛出去之前会执行processWorkerExit(w, completedAbruptly);:
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
processWorkerExit中是把当前运行的线程销毁workers.remove(w)、同时addWorker()新增一个 Worker 对象接着处理workQueue中的任务。Worker也是一个Runnable,run():
public void run() {
runWorker(this);
}
但此时,workQueue是空的,所以阻塞在getTask()方法上。
结论:线程池中一但抛出了未被捕获的异常时,线程池会回收当前的线程并创建一个新的 Worker,它也会一直不断的从队列里获取任务来执行。
博客转载自相关链接,分析线程池使用时,任务抛出未捕获异常的现象。通过代码模拟,发现线程抛出异常后,jstack显示有线程处于waiting状态。经分析,抛出异常时会销毁当前线程、新增Worker处理任务,若队列空则阻塞,结论是线程池会回收异常线程并创建新Worker从队列取任务执行。
947

被折叠的 条评论
为什么被折叠?



