zoukankan      html  css  js  c++  java
  • 线程池中的线程如果抛异常会怎么样

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

      一 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(),来检查是否有异常

       

      

  • 相关阅读:
    P2604 [ZJOI2010]网络扩容
    P2053 [SCOI2007]修车
    P2045 方格取数加强版
    P4134 [BJOI2012]连连看
    P2153 [SDOI2009]晨跑
    P3381 【模板】最小费用最大流
    P3376 【模板】网络最大流
    P1326 足球
    2020牛客多校第八场I题 Interesting Computer Game(并查集+判环)
    Codeforces 1375D Replace by MEX(思维题)
  • 原文地址:https://www.cnblogs.com/juniorMa/p/13955280.html
Copyright © 2011-2022 走看看