Обработка исключений для ThreadPoolExecutor

у меня есть следующий фрагмент кода, который в основном сканирует список задач, которые необходимо выполнить, и каждая задача затем передается исполнителю для выполнения.

на JobExecutor в свою очередь создает другого исполнителя (для выполнения db stuff...чтение и запись данных в очередь) и завершает задание.

JobExecutor возвращает Future<Boolean> для представленных задач. Когда одна из задач терпит неудачу, я хочу изящно прервать все потоки и выключить исполнителя ловлю все исключения. Какие изменения мне нужно сделать?

public class DataMovingClass {
    private static final AtomicInteger uniqueId = new AtomicInteger(0);

  private static final ThreadLocal<Integer> uniqueNumber = new IDGenerator();   

  ThreadPoolExecutor threadPoolExecutor  = null ;

   private List<Source> sources = new ArrayList<Source>();

    private static class IDGenerator extends ThreadLocal<Integer> {
        @Override
        public Integer get() {
            return uniqueId.incrementAndGet();
        }
  }

  public void init(){

    // load sources list

  }

  public boolean execute() {

    boolean succcess = true ; 
    threadPoolExecutor = new ThreadPoolExecutor(10,10,
                10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1024),
                new ThreadFactory() {
                    public Thread newThread(Runnable r) {
                        Thread t = new Thread(r);
                        t.setName("DataMigration-" + uniqueNumber.get());
                        return t;
                    }// End method
                }, new ThreadPoolExecutor.CallerRunsPolicy());

     List<Future<Boolean>> result = new ArrayList<Future<Boolean>>();

     for (Source source : sources) {
                    result.add(threadPoolExecutor.submit(new JobExecutor(source)));
     }

     for (Future<Boolean> jobDone : result) {
                try {
                    if (!jobDone.get(100000, TimeUnit.SECONDS) && success) {
                        // in case of successful DbWriterClass, we don't need to change
                        // it.
                        success = false;
                    }
                } catch (Exception ex) {
                    // handle exceptions
                }
            }

  }

  public class JobExecutor implements Callable<Boolean>  {

        private ThreadPoolExecutor threadPoolExecutor ;
        Source jobSource ;
        public SourceJobExecutor(Source source) {
            this.jobSource = source;
            threadPoolExecutor = new ThreadPoolExecutor(10,10,10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1024),
                    new ThreadFactory() {
                        public Thread newThread(Runnable r) {
                            Thread t = new Thread(r);
                            t.setName("Job Executor-" + uniqueNumber.get());
                            return t;
                        }// End method
                    }, new ThreadPoolExecutor.CallerRunsPolicy());
        }

        public Boolean call() throws Exception {
            boolean status = true ; 
            System.out.println("Starting Job = " + jobSource.getName());
            try {

                        // do the specified task ; 


            }catch (InterruptedException intrEx) {
                logger.warn("InterruptedException", intrEx);
                status = false ;
            } catch(Exception e) {
                logger.fatal("Exception occurred while executing task "+jobSource.getName(),e);
                status = false ;
            }
           System.out.println("Ending Job = " + jobSource.getName());
            return status ;
        }
    }
}   

3 ответов


когда вы отправляете задание исполнителю, он возвращает вам FutureTask экземпляра.

FutureTask.get() будет повторно бросать любое исключение, вызванное задачей как ExecutorException.

поэтому, когда вы перебираете List<Future> и звоните садитесь на каждого, ловите ExecutorException и вызовите упорядоченное завершение работы.


так как вы представляете задачи ThreadPoolExecutor, исключения проглатываются FutureTask.

посмотри код

**Inside FutureTask$Sync**

void innerRun() {
    if (!compareAndSetState(READY, RUNNING))
        return;

  runner = Thread.currentThread();
    if (getState() == RUNNING) { // recheck after setting thread
        V result;
       try {
            result = callable.call();
        } catch (Throwable ex) {
           setException(ex);
            return;
        }
       set(result);
    } else {
        releaseShared(0); // cancel
    }

}

protected void setException(Throwable t) {
   sync.innerSetException(t);
}

из вышеприведенного кода ясно, что setException способ ловли Throwable. По этой причине, FutureTask глотает все исключения если вы используете "submit()" метод on ThreadPoolExecutor

согласно java документация, вы можете продлить afterExecute() метод ThreadPoolExecutor

protected void afterExecute(Runnable r,
                            Throwable t) 

пример кода в документации:

class ExtendedExecutor extends ThreadPoolExecutor {
   // ...
   protected void afterExecute(Runnable r, Throwable t) {
     super.afterExecute(r, t);
     if (t == null && r instanceof Future<?>) {
       try {
         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)
       System.out.println(t);
   }
 }

вы можете поймать Exceptions тремя способами

  1. Future.get() как указано в принятой ответ
  2. оберните всю run() или call() метод try{}catch{}Exceptoion{} блоки
  3. переопределить afterExecute of ThreadPoolExecutor метод, как показано выше

чтобы изящно прервать другие потоки, посмотрите на вопрос ниже SE:

как чтобы остановить следующий поток от запуска в ScheduledThreadPoolExecutor

как принудительно завершить работу java ExecutorService


подкласс ThreadPoolExecutor и переопределить его protected afterExecute (Runnable r, Throwable t) метод.

Если вы создаете пул потоков через java.util.concurrent.Executors класс удобства (которого у вас нет), посмотрите на его источник, чтобы увидеть, как он вызывает ThreadPoolExecutor.