Java 中異步任務的實現 之 Callable,Future,FutureTask
這裏,我們主要用到的類和接口爲:Callable,Future,FutureTask
Runable只需關心運行的動作行爲,而Callable同時關心運行的結果。
package java.util.concurrent;
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Future表示一個任務的生命週期。提供接口來判斷任務完成,取消與否,也提供接口來獲取任務結果。
get
方法的行爲取決於任務的狀態(尚未開始,正在運行,已完成)
。
- 如果任務
已經完成
,那麼get會立即返回結果或者拋出異常(任務執行過程中發生異常、任務被取消也屬於任務已完成
) - 如果任務
沒有完成
,get將阻塞。 - 如果任務
拋出異常
,那麼get會將該異常封裝爲ExecutionException
重新拋出,可以通過getCause
來獲取最初的異常 - 如果任務
被取消
,那麼get將拋出CancellationException
package java.util.concurrent;
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
可以通過多種方法來創建一個Future任務
。
ExecutorService中所有submit
方法都返回一個Future。- 指定
Runnable或者Callable實例化一個FutureTask
(FutureTask實現了Runnable,因此可以將它提交給Executor來執行,或者直接調用它的run方法)
我們簡單看看FutureTask的代碼:
public class FutureTask<V> implements RunnableFuture<V> {
// 這裏不列出它的代碼了
}
// 其中RunnableFuture接口:
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
下面來測試一個Future
代碼例子,體驗一下異步任務:
@Slf4j
public class FutureExample {
/**
* 定義自己的Callable實現
* 它描述了任務怎麼執行,以及執行的返回結果
*/
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
log.info("do something in callable");
// 異步任務執行5秒鐘
Thread.sleep(5000);
return "Done";
}
}
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
// 提交Callable到ExecutorService,會返回一個Future
Future<String> future = executorService.submit(new MyCallable());
log.info("do something in main");
// 主任務提交任務後執行1秒鐘,然後通過get等待執行結果
Thread.sleep(1000);
String result = future.get();
log.info("result:{}", result);
}
}
// 順帶討論一下Lambda表達式怎麼簡化程序代碼:
@Slf4j
public class FutureExample {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit( ()->{ // Callable
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
});
log.info("do something in main");
Thread.sleep(1000);
String result = future.get();
log.info("result:{}", result);
}
}
我們運行,看一看運行結果如下,可以看到,43秒開始等待運行結果,5秒後的48秒返回結果"Done"
11:46:43.086 [pool-1-thread-1] INFO FutureExample - do something in callable
11:46:43.086 [main] INFO FutureExample - do something in main
11:46:48.092 [main] INFO FutureExample - result:Done
同樣,我們使用FutureTask
也能達到同樣的效果:
@Slf4j
public class FutureTaskExample {
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
}
});
new Thread(futureTask).start();
log.info("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
log.info("result:{}", result);
}
}
// 同樣,我們嘗試Lambda表達式重寫一下:
@Slf4j
public class FutureTaskExample {
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<>(()->{
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
});
new Thread(futureTask).start();
log.info("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
log.info("result:{}", result);
}
}