В каких случаях Future.get() бросает ExecutionException или InterruptedException

Мой фрагмент кода:

ExecutorService executor = Executors.newSingleThreadExecutor();
try {
    Task t = new Task(response,inputToPass,pTypes,unit.getInstance(),methodName,unit.getUnitKey());
    Future<SCCallOutResponse> fut = executor.submit(t);
    response = fut.get(unit.getTimeOut(),TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
    // if the task is still running, a TimeOutException will occur while fut.get()
    cat.error("Unit " + unit.getUnitKey() + " Timed Out");
    response.setVote(SCCallOutConsts.TIMEOUT);
} catch (InterruptedException e) {
    cat.error(e);
} catch (ExecutionException e) {
    cat.error(e);
} finally {
    executor.shutdown();
}

Как я должен обрабатывать InterruptedException и ExecutionException в коде?

И в каких случаях эти исключения выбрасываются?

Ответы

Ответ 1

ExecutionException и InterruptedException - это две очень разные вещи.

ExecutionException упаковывает любое исключение, которое генерирует выполняемый поток, поэтому, если ваш поток, например, выполнял какой-то ввод-вывод, который вызывал IOException, он был бы помещен в ExecutionException и переброшен.

InterruptedException не является признаком того, что что-то пошло не так. Он предназначен для того, чтобы дать вам знать, когда пора остановиться, чтобы они могли закончить свою текущую работу и выйти изящно. Скажем, я хочу, чтобы мое приложение перестало работать, но я не хочу, чтобы мои потоки отбрасывали то, что они делают, в середине чего-то (что случилось бы, если бы я сделал их потоками демона). Поэтому, когда приложение закрывается, мой код вызывает метод прерывания в этих потоках, который устанавливает для них флаг прерывания, и в следующий раз, когда эти потоки ожидают или спят, они проверяют флаг прерывания и выдают InterruptedException, которое я могу использовать выручить из любой логики обработки/ожидания бесконечного цикла, в которой заняты потоки. (И если поток не ждет или не спит, он может просто периодически проверять флаг прерывания.) Таким образом, это экземпляр используемого исключения изменить логический поток. Единственная причина, по которой вы могли бы занести его в журнал, - это пример программы, чтобы показать вам, что происходит, или если вы отлаживаете проблему, где логика прерываний работает неправильно.

Ответ 2

InterruptedException будет вызываться, если interrupt вызывается в ожидающем потоке до завершения вычисления.

ExecutionException будет выбрано, если вовлеченное вычисление (Task в этом случае) выдает исключение.

Как вы хотите справиться с этим, это будет полностью зависеть от вашего приложения.

EDIT: здесь демонстрация прерывания:

import java.util.concurrent.*;

public class Test
{
    public static void main(String[] args) throws Exception
    {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Future<String> future = executor.submit(new SlowCallable());
        executor.submit(new Interruptor(Thread.currentThread()));
        try
        {
            System.out.println(future.get());
        }
        catch (InterruptedException e)
        {
            System.out.println("I was interrupted");
        }
    }

    private static class Interruptor implements Callable<String>
    {
        private final Thread threadToInterrupt;

        Interruptor(Thread threadToInterrupt)
        {
            this.threadToInterrupt = threadToInterrupt;
        }

        public String call() throws Exception
        {
            Thread.sleep(2000);
            threadToInterrupt.interrupt();
            return "interrupted other thread";
        }
    }

    private static class SlowCallable implements Callable<String>
    {
        public String call() throws Exception
        {
            Thread.sleep(5000);
            return "finished";
        }
    }
}

Ответ 4

Пример кода для возврата трех типов исключений.

import java.util.concurrent.*;
import java.util.*;

public class ExceptionDemo{
    public static void main(String args[]){
        int poolSize=1;
        int maxPoolSize=1;
        int queueSize=30;
        long aliveTive=60;
        ArrayBlockingQueue<Runnable> queue= new ArrayBlockingQueue<Runnable>(queueSize);
        ThreadPoolExecutor executor= new ThreadPoolExecutor(poolSize,maxPoolSize,aliveTive,
                        TimeUnit.MILLISECONDS,queue);
        List<Future> futures = new ArrayList<Future>();
        for ( int i=0; i < 5; i++){
            futures.add(executor.submit(new RunnableEx()));
        }
        for ( Iterator it = futures.iterator(); it.hasNext();){
            try {
                Future f = (Future)it.next();
                f.get(4000,TimeUnit.MILLISECONDS);
            }catch(TimeoutException terr){
                System.out.println("Timeout exception");
                terr.printStackTrace();
            }
            catch(InterruptedException ierr){
                System.out.println("Interrupted exception:");
                ierr.printStackTrace();
            }catch(ExecutionException err){
                System.out.println("Exeuction exception:");
                err.printStackTrace();
                Thread.currentThread().interrupt();
            }
        }
        executor.shutdown();
    }
}

class RunnableEx implements Runnable{
    public void run() {
        // code in here
        System.out.println("Thread name:"+Thread.currentThread().getName());
        try{
            Random r = new Random();
            if (r.nextInt(2) == 1){
                Thread.sleep(2000);
            }else{
                Thread.sleep(4000);
            }
            System.out.println("eee:"+1/0);
        }catch(InterruptedException irr){
            irr.printStackTrace();
        }
    }
}

выход:

Thread name:pool-1-thread-1
Timeout exception
Thread name:pool-1-thread-1
java.util.concurrent.TimeoutException
        at java.util.concurrent.FutureTask.get(FutureTask.java:201)
        at ExceptionDemo.main(ExceptionDemo.java:20)
Thread name:pool-1-thread-1
Exeuction exception:
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
        at java.util.concurrent.FutureTask.report(FutureTask.java:122)
        at java.util.concurrent.FutureTask.get(FutureTask.java:202)
        at ExceptionDemo.main(ExceptionDemo.java:20)
Caused by: java.lang.ArithmeticException: / by zero
        at RunnableEx.run(ExceptionDemo.java:49)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
        at java.util.concurrent.FutureTask.run(FutureTask.java:262)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:744)
Interrupted exception:
java.lang.InterruptedException
        at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:400)
        at java.util.concurrent.FutureTask.get(FutureTask.java:199)
        at ExceptionDemo.main(ExceptionDemo.java:20)
Timeout exception
java.util.concurrent.TimeoutException
        at java.util.concurrent.FutureTask.get(FutureTask.java:201)
Thread name:pool-1-thread-1
        at ExceptionDemo.main(ExceptionDemo.java:20)
Thread name:pool-1-thread-1
Timeout exception
java.util.concurrent.TimeoutException
        at java.util.concurrent.FutureTask.get(FutureTask.java:201)
        at ExceptionDemo.main(ExceptionDemo.java:20)

TimeoutException: Исключение, возникающее при сбое операции блокировки.

В приведенном выше примере некоторые задачи занимают больше времени (из-за спящего режима на 4 секунды) и операции блокировки get() на Future

Увеличьте время ожидания или оптимизируйте Runnable task.

ExecutionException: Исключение, возникающее при попытке получить результат задачи, которая прервана путем выброса исключения = > Вычисление бросило исключение

В приведенном выше примере этот Exception моделируется через ArithmeticException: / by zero

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

InterruptedException: выброшено, когда поток ожидает, спит или иным образом занят, а поток прерывается до или во время активность.

В приведенном выше примере этот Exception моделируется путем прерывания текущего потока во время ExecutionException.

Как правило, вы должны поймать его, не действуйте на нем.