preface
Author Mahua, public number [source interest circle], touched the piano under cooking, a program ape, not like a program ape, focus on the framework source code, concurrent programming, Python and other technologies
How to handle exceptions in the JDK thread pool
Working with a thread pool and not knowing how to handle exceptions or how to properly handle exceptions thrown
1. Read with questions
How does the thread pool print out exceptions thrown by running tasks?
2. Do thread pools execute() and submit() handle exceptions the same?
3. What are the ways to handle task exceptions?
According to the above problems, through the sample code and source code common parsing
Unless otherwise noted, use JDK 1.8 for reading
2. How do I handle exceptions thrown when a task is running
Execute () is an example of how this is handled in the source code
If you have read the previous two thread pool articles, the first task execution process is relatively clear
execute() -> addWorker() -> start() -> run() -> runWorker()
Put the ThreadPoolExecutor#runWorker source code directly here
final void runWorker(Worker w) {
. try {
while(task ! =null|| (task = getTask()) ! =null) {
w.lock();
. try { beforeExecute(wt, task); Throwable thrown = null; try { / * ** Run the run method of the submitted task* If an exception is thrown, it will be caught by the following catch block* / 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); } } Copy the code
When a task submitted to the thread pool executes an exception, it is caught by the catch block below and throws an exception to the JVM
Let’s look at the following test code. The logic is simple: create a thread pool and throw a runtime exception when submitting the first task
Don’t worry about the thread pool build parameters in the sample code
@Test
public void executorTest(a) {
ThreadPoolExecutor pool =
new ThreadPoolExecutor(1.3.1000, TimeUnit.HOURS, new LinkedBlockingQueue(5));
pool.execute(() -> {
int i = 1/0; }); pool.shutdown(); } / * * * Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero * at cn.hsa.tps.ThreadPoolExceptionTest.lambda$executorTest$0(ThreadPoolExceptionTest.java:22) * 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) * / Copy the code
Throwing an exception is understandable, but inside the thread pool we just throw an exception. How do we print the exception information?
With doubts, I searched the all-powerful “Baidu” and finally got the answer:
The exception thrown up will be invoked by the virtual machine 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); } Copy the code
We can also look at comments to get relevant information
To an uncaught exception handler distribution, dispatchUncaughtException method can only be the JVM is taken
The “program” that handles the uncaught exception is UncaughtExceptionHandler
Continue to check the relevant methods, Thread# getUncaughtExceptionHandler
public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler(a) {
returnuncaughtExceptionHandler ! =null ?
uncaughtExceptionHandler : group;
}
Copy the code
This checks to see if the thread has an uncaptured processing policy, and if it does not, it executes using the policy of the default thread group
private ThreadGroup group;
public class ThreadGroup implements Thread.UncaughtExceptionHandler {... }Copy the code
Thread groups involve batch management threads, such as batch stop or suspend, which will not be discussed here
ThreadGroup#uncaughtException ThreadGroup#uncaughtException ThreadGroup#uncaughtException
public void uncaughtException(Thread t, Throwable e) {
if(parent ! =null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler(); if(ueh ! =null) { ueh.uncaughtException(t, e); } else if(! (einstanceof ThreadDeath)) { // This will eventually be called to print a stack of information System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err); } } } Copy the code
It’s basically just swallowing the exception, but it prints out the exception, and it tells us, where does the exception that was thrown from the thread pool end up
3. Execute () and submit() check whether exception processing is consistent
Submit () instead of execute()
@Test
public void executorTest(a) {
ThreadPoolExecutor pool =
new ThreadPoolExecutor(1.3.1000, TimeUnit.HOURS, new LinkedBlockingQueue(5));
pool.submit(() -> {
int i = 1/0; }); pool.shutdown(); } Copy the code
Clear friends believe that when you see here, you will know that there will be no abnormal information printing
Why won’t it print? This problem follows the source code we look together
You can see that the Task is no longer a related Runnable object, but a FutureTask
Moving on to see how the FutureTask source code executes
public void run(a) {
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 { // The run method is also called internally result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { .} Copy the code
According to the source code, exceptions thrown by the execution process will be caught, so the exception information will not be printed by default
How do I know if submit() throws an exception?
The return value of submit() is the Future interface. The default implementation is FutureTask, and there is a get() method inside that can both get the return value and throw the corresponding exception
public V get(a) throws InterruptedException, ExecutionException {
int s = state;
// If a new or unfinished Future is blocked
if (s <= COMPLETING)
s = awaitDone(false.0L);
/ / 🚩 focus return report(s); } private V report(int s) throws ExecutionException { Object x = outcome; // The result is returned after normal completion if (s == NORMAL) return (V) x; // The task was cancelled and an exception was thrown if (s >= CANCELLED) throw new CancellationException(); // This is an internal execution error throw new ExecutionException((Throwable) x); } Copy the code
When a task is abnormal, an exception is reported. You can use the GET () method to return the task execution exception
4. What are the methods to handle task exceptions
1. Inside the task, try catch any step that might throw an exception
@Test
public void executorTest(a) {
ThreadPoolExecutor pool =
new ThreadPoolExecutor(1.3.1000, TimeUnit.HOURS, new LinkedBlockingQueue(5));
pool.execute(() -> {
try { int i = 1/0; } catch (Exception ex) { . } }); pool.shutdown(); } Copy the code
Handle exceptions in a catch block, retry handling, or alarm exceptions
The same goes for the submit
2, override the thread UncaughtExceptionHandler
UncaughtExceptionHandler is used to handle the uncaught exception policy when creating a thread factory
@Test
public void executorTest(a) {
Thread.UncaughtExceptionHandler sceneHandler = new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
// Just for example, different scenarios are different log.error(">>> Thread :: {}, exception handling... ", t.getName(), e); } }; ThreadFactory threadFactory = new ThreadFactoryBuilder() .setUncaughtExceptionHandler(sceneHandler) .setDaemon(true).setNameFormat("Named after thread pool function").build(); ThreadPoolExecutor pool = new ThreadPoolExecutor(1.3.1000, TimeUnit.HOURS, new LinkedBlockingQueue(5), threadFactory); pool.execute(() -> { int i = 1 / 0; }); pool.shutdown(); } Copy the code
The sample code is also relatively simple, customizes UncaughtExceptionHandler processing policy, specifies the custom processing policy when creating the thread factory, and assigns the thread factory to the thread pool
Override afterExecute method in thread pool
@Test
public void executorExceptionAfterExecuteTest(a) {
ThreadPoolExecutor pool =
new ThreadPoolExecutor(1.3.1000, TimeUnit.HOURS, new LinkedBlockingQueue(5)) {
@Override
public void afterExecute(Runnable r, Throwable t) { // Just for example, different scenarios are different log.error(">>> Exception handling... ", t); } }; .} Copy the code
4.1 Summary of Exception Handling
Due to the granularity and unfriendly processing of the second and third methods above, try catch in the first task can be directly used in daily work
Afterword.
Due to the limited level of the author, you are welcome to feedback and correct the article is not correct, thanks to 🙏
The likes of my friends are the biggest support for me. If I gain something from reading this article, I hope I can like it, comment on it and pay attention to it.
This article is formatted using MDNICE