线程池中的线程如果抛异常会怎么样

看到一篇不错的文章,让我有思路也写写关于线程池,本文主要关注线程池里的任务跑了异常怎么办

  一 Thread能捕获异常吗

public class ThreadExceptionTest {
    
    public static class InitialtiveThread implements Runnable
    {
        @Override
        public void run()
        {
            System.out.println(3/2);
            System.out.println(3/0);
            System.out.println(3/1);
        }
    }
    
    public static void main(String[] args) {
        
        InitialtiveThread runner = new InitialtiveThread();
        try {
            Thread thread = new Thread(runner);
            thread.start();
        } catch(Exception e) {
            
        }
        
    }

}

  执行之后发现,如果run方法里抛出异常,通过try,catch 线程的start方法是没法捕获异常的,那是因为该异常是一个UncaughtException,没法通过用户的代码捕获的。

  处理方法有两种

   1 run方法里面捕获

  2  thread.setUncaughtExceptionHandler((Thread t, Throwable e) -> {System.out.println(t.getName() + ": " + e.getMessage());}); 

  方法2就是给线程指定对于UncaughtException的处理器

  上述的知识作为前提,为了更好地理解下面的内容

  二 线程池执行的情况

   首先要区分execute和submit两种方式,这两种方式如果没看过源码,可能还真以为差不多,其实差多了

   先说execute,看代码段

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);

  对于execute方式,用户提交的Runnable没有经过任何的转换,保留原样

  现在看Worker的runWorker

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);
        }

  主动throw了异常,经过第一节的知识,一定会抛出来UncaughtException

  我们先看Finally分支

private void processWorkerExit(Worker w, boolean completedAbruptly) {//抛异常的情况completedAbruptly是false的
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);//移除该worker
        } 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);//如果小于核心线程 再次添加
        }
    }

  对于execute方式,如果抛出异常,就把该线程移除,如果线程池当前的worker数量不到coreSize,就再添加一个

  那么,如果增加了 setUncaughtExceptionHandler 会怎么样呢?

  通过跟代码,run会抛出异常,所以还是会走线程移除流程,只不过之前是把异常抛出来,现在是handler里面的处理,但是线程还是会终止的。

  

  再来看submit

  submit就好玩了,这里也不详细的跟代码了,简单点说,submit会把传进来的Runnable或者Callable包成FutureTask,这个FutureTask本身还实现了Runnable接口

  所以我们看看他的run

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 must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

  看得出来,这个run里面是有try catch包装的,这样就不会有异常了。

  不过使用submit还是要注意,用了submit后即使是有 setUncaughtExceptionHandler 也是没用的,因为里面catch了。所以submit方式会吞异常,使用submit方式必须手动执行get(),

  看看是否有异常

   

  

上一篇:Spark学习之路 (十六)SparkCore的源码解读(二)spark-submit提交脚本


下一篇:关于Android依赖注入框架ButterKnife和Dagger的简单比较