Время ожидания относится к вызывающему методу get
с таймаутом и только для этого вызывающего.Тайм-аут нигде не означает отмены.Например, следующий код является допустимым использованием Future
API:
ExecutorService es = Executors.newSingleThreadExecutor();
Future<String> f = es.submit(() -> {
Thread.sleep(3000);
return "hello";
});
for(;;) try {
String s = f.get(500, TimeUnit.MILLISECONDS);
System.out.println("got "+s);
break;
}
catch(TimeoutException ex) {
// perhaps, do some other work
System.out.println("will wait something more");
}
catch (ExecutionException ex) {
System.out.println("failed with "+ex);
break;
}
es.shutdown();
Привязка очистки к методам, фактически предназначенным для запроса результата, не является полезным подходом.Тайм-аут, предоставляемый вызывающим абонентом этого метода, не относится к фактической операции.Нет даже гарантии, что результат будет запрошен до завершения операций или что он вообще будет запрошен.
Очистка должна происходить, когда либо операция завершена, либо когда будущее явно отменено.Если вызывающая сторона намерена отменить запрос после истечения времени ожидания, вызывающая сторона должна вызвать cancel
только после перехвата TimeoutException
.
Один из подходов, на который часто указывают, заключается в использовании CompletionService
, например,
* 1015.*
Вы контролируете, когда опрашивать завершенные фьючерсы, как в другом фоновом потоке, как показано в примере, или непосредственно перед началом новых заданий.
Вы можете проверить это как
Future<String> f = doSomeWork();
try {
String s = f.get(500, TimeUnit.MILLISECONDS);
System.out.println("got "+s);
}
catch(TimeoutException ex) {
System.out.println("no result after 500ms");
}
catch (ExecutionException ex) {
System.out.println("failed with "+ex);
}
if(f.cancel(true)) System.out.println("canceled");
f = doSomeWork();
// never calling get() at all
Но, честно говоря, я никогда не понимал, почему такие сложные вещи действительно необходимы.Если вы хотите очистить в нужное время, вы можете использовать
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
public static Future<String> doSomeWork() {
Callable<String> actualJob = () -> {
Thread.sleep(3000);
return "hello";
};
FutureTask<String> ft = new FutureTask<>(actualJob) {
@Override
protected void done() {
System.out.println("cleanup "+this);
}
};
MY__EXECUTOR.execute(ft);
return ft;
}
для достижения того же.
Или даже проще
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
public static Future<String> doSomeWork() {
Callable<String> actualJob = () -> {
Thread.sleep(3000);
return "hello";
};
return MY__EXECUTOR.submit(() -> {
try {
return actualJob.call();
}
finally {
// perform cleanup
System.out.println("cleanup");
}
});
}
В любом случае,очистка будет выполнена независимо от того, было ли задание выполнено успешно, не выполнено или отменено.Если использовалось cancel(true)
, и фактическое задание поддерживает прерывание, очистка также будет выполнена сразу после.