併發編程實戰(4). 異步任務 之 Callable,Future,FutureTask

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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章