Java线程池中的异常处理

Java线程池中的异常处理

原文博客

§ 前置知识

  • 线程池中的任务有两种,一种有返回值,一种无返回值。通常对应着两种提交任务的方法:

    • submit方法:虽然参数是Runnable,但由于返回值为Future,所以通常传入的参数为FutureTask类的对象。(FutureTask间接实现了Runnable接口和Future接口)

      public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
      }
      
    • execute方法:

      public void execute(Runnable command) {
        if (command == null)
          throw new NullPointerException();
        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);
      }
      
  • 不论是什么类型的任务,最终被包装如ThreadPoolExecutor类中的Worker类,而任务的执行都是通过runWorker()方法,这里截取关键部分:

    try {
      beforeExecute(wt, task);
      Throwable thrown = null;
      try {
        task.run(); // 调用对应任务的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();
    }
    

§ 默认情况存在的问题

由前置知识可知,runWorker()方法内,若task.run()出现异常,会抛出,然后进入afterExecute(task, thrown)方法,而该方法默认为空,所以默认情况下我们无法得到想要的异常信息。针对submit和execute提交的有返回值和无返回值的任务,有两种解决方向如下。

§ submit提交的有返回值的任务

由于任务是FutureTask类的,所以在执行task.run()时,执行的是FutureTask中重写的run()方法,截取关键部分如下:

try {
  result = c.call();
  ran = true;
} catch (Throwable ex) {
  result = null;
  ran = false;
  setException(ex);
}
//////
protected void setException(Throwable t) {
  if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
    outcome = t; // !!!!关键
    UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
    finishCompletion();
  }
}

***可见,任务执行出现异常后,不抛出,而是存入返回值当中。因此,若要获取异常信息则需通过返回值获取,所以可以在try catch中通过FutureTask的get()方法获取返回结果(异常):***

try{
	future.get();
}catch(xxxException){
	// do sth
}

§ 通过execute提交的无返回值的任务

【法1】

从前面所讲可以知道,无返回值的任务,即原生Runnable,在执行task.run()时若有异常则会抛出,并只能在afterExecute(task, thrown)中进行自定义的处理,***所以可以自定义线程池,继承ThreadPoolExecutor并复写其afterExecute(Runnable r, Throwable t)方法。***


【法2】

***实现Thread.UncaughtExceptionHandler接口,并重写实现void uncaughtException(Thread t, Throwable e);方法,在该方法中处理异常。并将该handler传递给线程池的ThreadFactory。***

具体可参考:https://juejin.im/post/5d27c3e6518825451f65ee15

你可能感兴趣的:(JAVA,JAVA后端学习)