先上两段代码:
1 2 3 4 5 6 7 8 9 ExecutorService threadPool = Executors.newFixedThreadPool(1 );threadPool.execute(() -> { System.out.println("execute" ); Object obj = null ; System.out.println(obj.toString()); }); threadPool.shutdown(); System.out.println("**********" );
1 2 3 4 5 6 7 8 9 ExecutorService threadPool = Executors.newFixedThreadPool(1 );threadPool.submit(() -> { System.out.println("submit" ); Object obj = null ; System.out.println(obj.toString()); }); threadPool.shutdown(); System.out.println("**********" );
运行结果发现,execute执行的时候抛出了预期的NullPointerException异常,而submit执行时什么都没有!
三个字:跟源码。
1 2 3 4 5 6 public Future<?> submit(Runnable task) { if (task == null ) throw new NullPointerException (); RunnableFuture<Void> ftask = newTaskFor(task, null ); execute(ftask); return ftask; }
使用submit提交任务时,会先把task包装成FutureTask对象,然后才去调用execute来执行任务。继续看execute:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void execute (Runnable command) { 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提交到workQueue中。来看Worker代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private final class Worker extends AbstractQueuedSynchronizer implements Runnable { final Thread thread; Runnable firstTask; volatile long completedTasks; Worker(Runnable firstTask) { setState(-1 ); this .firstTask = firstTask; this .thread = getThreadFactory().newThread(this ); } public void run () { runWorker(this ); } }
忽略一些细节的地方,Worker就是一个Runnable,run()方法调用的是runWorker():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 final void runWorker (Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null ; w.unlock(); boolean completedAbruptly = true ; try { while (task != null || (task = getTask()) != null ) { w.lock(); 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); } }
execute的方式提交时,这里的task就是Runnable(上面代码有),因此就会直接抛出,最终JVM会去调用Thread#dispatchUncaughtException方法:
1 2 3 4 5 6 7 8 private void dispatchUncaughtException (Throwable e) { getUncaughtExceptionHandler().uncaughtException(this , e); }
1 2 3 4 public UncaughtExceptionHandler getUncaughtExceptionHandler () { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; }
一般没有去设置uncaughtExceptionHandler 时,getUncaughtExceptionHandler()返回的是group,即ThreadGroup实例,来看ThreadGroup#uncaughtException:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 (!(e instanceof ThreadDeath)) { System.err.print("Exception in thread \"" + t.getName() + "\" " ); e.printStackTrace(System.err); } } }
可以看到,先看有没有parent,没有则通过Thread.getDefaultUncaughtExceptionHandler()获取UncaughtExceptionHandler来处理,没有的话才将异常打印到System.err里面。
那么submit的方式提交时,是怎么的情况呢?runWorker()中task是FutureTask,它的run()方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 = null ; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
1 2 3 4 5 6 7 8 protected void setException (Throwable t) { if (UNSAFE.compareAndSwapInt(this , stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this , stateOffset, EXCEPTIONAL); finishCompletion(); } }
可以看出,对于异常并没有抛出,而是把异常给了outcome,这样在调用get()方法的时候,才会拿到这个异常。
OK,到此为止已经解释了最开始的两段代码运行结果不同的原因了。
在写代码时对于线程池、包括线程的异常处理最好是直接try/catch。 除此之外还有其它的方式,但是不推荐:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ExecutorService threadPool = Executors.newFixedThreadPool(1 , new ThreadFactory () { @Override public Thread newThread (Runnable r) { Thread t = new Thread (r); t.setUncaughtExceptionHandler(new Thread .UncaughtExceptionHandler() { @Override public void uncaughtException (Thread t, Throwable e) { System.out.println("uncaughtException" ); System.out.println(e); } }); return t; } }); threadPool.execute(() -> { System.out.println("execute" ); Object obj = null ; System.out.println(obj.toString()); }); threadPool.shutdown(); System.out.println("**********" ); }