В каких случаях 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";
}
}
}
Ответ 3
В статье IBM Developer Works "Работа с InterruptedException" есть несколько советов о том, как обращаться с InterruptedException
.
Ответ 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
.
Как правило, вы должны поймать его, не действуйте на нем.