JUC併發編程學習(十五)-異步回調之CompletableFuture

Future

Future接口是Java多線程Future模式的實現,在java.util.concurrent包中,可以用來進行異步計算。

Future模式是多線程中一種常用的模式。可以理解爲:我有一個任務,把它交給Future去完成。在這期間我可以去做自己想幹的事情,過段時間後,我可以直接去Future那提取結果。

Future的接口主要有五個方法。

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接口的方法介紹如下:

  • boolean cancel (boolean mayInterruptIfRunning) 取消任務的執行。參數指定是否立即中斷任務執行,或者等等任務結束
  • boolean isCancelled () 任務是否已經取消,任務正常完成前將其取消,則返回 true
  • boolean isDone () 任務是否已經完成。需要注意的是如果任務正常終止、異常或取消,都將返回true
  • V get () throws InterruptedException, ExecutionException 等待任務執行結束,然後獲得V類型的結果。InterruptedException 線程被中斷異常, ExecutionException任務執行異常,如果任務被取消,還會拋出CancellationException
  • V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一樣,多了設置超時時間。參數timeout指定超時時間,uint指定時間的單位,在枚舉類TimeUnit中有相關的定義。如果計算超時,將拋出TimeoutException

一般情況下,我們會結合Callable和Future一起使用,通過ExecutorService的submit方法執行Callable,並返回Future。

        ExecutorService executor = Executors.newCachedThreadPool();

        Future<String> future = executor.submit(() -> { //Lambda 是一個 callable, 提交後便立即執行,這裏返回的是 FutureTask 實例
            System.out.println("running task");
            Thread.sleep(10000);
            return "return task";
        });

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }

        System.out.println("do something else");  //前面的的 Callable 在其他線程中運行着,可以做一些其他的事情

        try {
            System.out.println(future.get());  //等待 future 的執行結果,執行完畢之後打印出來
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {

        } finally {
            executor.shutdown();
        }

比起future.get(),其實更推薦使用get (long timeout, TimeUnit unit) 方法,設置了超時時間可以防止程序無限制的等待future的結果。

Future模式的缺點

  • Future雖然可以實現獲取異步執行結果的需求,但是它沒有提供通知的機制,我們無法得知Future什麼時候執行完成
  • 要麼使用阻塞,在future.get()方法的地方等待future返回的結果,這時又變成了同步操作。要麼使用isDone()輪詢判斷Future是否完成。單這樣會耗費CPU資源(CPU空轉),於是completableFuture應運而生。

CompletableFuture

從Java 8開始引入了CompletableFuture,它針對Future做了改進,可以傳入回調對象,當異步任務完成或者發生異常時,自動調用回調對象的回調方法。

在這裏插入圖片描述
我們以異步調用無返回值爲例,看看如何使用CompletableFuture:

package com;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @className:
 * @author: youjp
 * @create: 2020-06-21 17:19
 * @description:    異步回調,completableFuture,對將來的結果進行結果,ajax請求就是一種異步回調!
 * 1.異步回調
 * 2.成功回調
 * 3.失敗回調
 * @Version: 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //無返回值的異步回調測試
        CompletableFuture<Void> completableFuture=CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":沒有返回結果的回調");
        });

        System.out.println(111);

        System.out.println(completableFuture.get());    //獲取阻塞執行結果:即沒有獲取到執行結果前,程序一直阻塞執行

    }

}



可以查看到,因爲是異步執行的,所以在爲獲取結果前,先執行了主線程。然後再獲取結果的同時阻塞去獲取執行結果。
在這裏插入圖片描述
CompletableFuture有返回結果情況測試

package com;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @className:
 * @author: youjp
 * @create: 2020-06-21 17:19
 * @description:    異步回調,completableFuture,對將來的結果進行結果,ajax請求就是一種異步回調!
 * 1.異步回調
 * 2.成功回調
 * 3.失敗回調
 * @Version: 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //有返回值的supplyAsync 異步回調
        //在ajax請求中,有成功和失敗的回調,成功即返回成功的數據,錯誤就返回錯誤的數據
        CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+":異步回調,返回integer");
            return 200;
        });

        //回調成功即 success
        System.out.println("異步調用返回結果:"+completableFuture.whenComplete((x,y)->{
            System.out.println("x-即success成功返回的結果:"+x); //正常返回結果
            System.out.println("y-即error錯誤返回的:"+y); //錯誤返回結果
        }).exceptionally(e->{
            System.out.println("獲取異常信息"+e.getMessage());
            return 500; //返回異常結果
        }).get());

    }

}


調用成功,則獲取到返回的值
在這裏插入圖片描述
設立一個程序算法異常,查看異常回調的結果

package com;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @className:
 * @author: youjp
 * @create: 2020-06-21 17:19
 * @description:    異步回調,completableFuture,對將來的結果進行結果,ajax請求就是一種異步回調!
 * 1.異步回調
 * 2.成功回調
 * 3.失敗回調
 * @Version: 1.0
 */
public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //有返回值的supplyAsync 異步回調
        //在ajax請求中,有成功和失敗的回調,成功即返回成功的數據,錯誤就返回錯誤的數據
        CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+":異步回調,返回integer");
            int num=400/0;
            return 200;
        });

        //回調成功即 success
        System.out.println("異步調用返回結果:"+completableFuture.whenComplete((x,y)->{
            System.out.println("x-即success成功返回的結果:"+x); //正常返回結果
            System.out.println("y-即error錯誤返回的:"+y); //錯誤返回結果
        }).exceptionally(e->{
            System.out.println("獲取異常信息"+e.getMessage());
            return 500; //返回異常結果
        }).get());

    }

}

在這裏插入圖片描述

CompletableFuture源碼解析

completableFuture源碼解析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章