Preface
When using the thread pool, I was always confused about the handling of exceptions. Sometimes, there is an exception output, sometimes not. I made a summary today.
practice
ExecutorService exec1 = Executors.newFixedThreadPool(1); exec1.submit(()->{ Object obj = null; System.out.println(obj.toString()); }); //The above method cannot output an exception. The exception has been swallowed exec1.submit(()->{ try { Object obj = null; System.out.println(obj.toString()); } catch (Exception e) { e.printStackTrace(); } }); // Add try catch to output exception 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()); }); //Can output exception 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<Runnable>()) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (t == null && r instanceof Future<?>) { try { //get first checks the status of the task, and then wraps the above exception as 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){ //exception handling t.printStackTrace(); } } }; executor3.execute(()->{ Object obj = null; System.out.println(obj.toString()); }); //Can output exception 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)
Summary
- The thread pool uses the execute() method to output exceptions, but uses the submit() method, without any other processing, the exceptions will be swallowed. What's the difference between using execute and submit in the specific thread pool? Let's talk about it. If you can use execute, don't use submit.
- Try to catch exceptions directly. I think it's more convenient and commonly used.
- When creating a thread pool, override the protected void afterExecute(Runnable r, Throwable t) {} method.
@Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (t == null && r instanceof Future<?>) { try { //get first checks the status of the task, and then wraps the above exception as 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){ //exception handling t.printStackTrace(); } }
principle
Why are submit exceptions swallowed and execute not?
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); //The incoming task will be encapsulated as futureTask, and then execute will also be called RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; }
/** * The code is still fresh, a typical producer / consumer model, * The details will not be tangled here for the moment, so if the submission to workQueue is successful, who is the consumer? * Obviously, you can study the same details in this new worker by yourself. Here we will find out * The core is the inner class of Worker */ public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) //The task will be added to the work in the execute method 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 { //Call the run method of the task 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); } }
The run method invoked in submit is different from execute, submit is run of FutureTask class, and execute is run method of runnable.
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); //An exception is handled instead of being thrown } if (ran) set(result); } } finally { } }