Java8 CompletableFuture使用

一、Future接口
  • 1.1 Runnable与Callable

Runnable接口源自JDK1.1,它只有一个run()方法,该方法没有返回结果:

public interface Runnable {
    public abstract void run();
}

Callable接口是JDK1.5中添加,只有一个call()方法,该方法支持结果返回且可以抛出异常:

public interface Callable<V> {
    V call() throws Exception;
}

无论是Runnalble还是Callable任务实例,直接调用时不会新开启子线程,是在主线程中运行。如果要在子线程中运行这些任务,需要将任务提交到Thread实例或线程池执行。值得注意的是通过Thread实例执行Callable任务,Thread没有接受Callable入参的构造函数,因此,不能直接构造Callable任务的Thread实例。可以通过构建FutureTask任务将Callable任务提供到Thread实例执行,因为FutureTask实现了Runnable接口,且其构造函数入参支持Callable类型。

	@Test
    public void testRunable() throws Exception {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000L);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                }
            }
        };

        Callable<Integer> c = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                try {
                    Thread.sleep(1000L);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                }
                return ThreadLocalRandom.current().nextInt(100);
            }
        };

        r.run();
        System.out.println(c.call());

        Thread t = new Thread(r);
        t.start();
        t.join(); // 不阻塞时,如果主线程比子线程先完成,则子线程中任务不会完整执行

        FutureTask<Integer> f = new FutureTask<>(c);
        Thread t2 = new Thread(f);
        t2.start();
        t2.join(); // 不阻塞时,如果主线程比子线程先完成,则子线程中任务不会完整执行
        System.out.println(f.get());

        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(r);
        Future<Integer> future = executorService.submit(c);
        System.out.println(future.get()); // 不阻塞时,如果主线程比子线程先完成,则子线程中任务不会完整执行
    }
---
main
main
62
Thread-0
Thread-1
6
pool-1-thread-1
pool-1-thread-1
36

注意:通过junit对以上代码进行测试时,如果主线程比子线程先执行完成,则主线程执行完成后会程序会退出,意味着没有完成的子线程任务不会再继续执行。因此,如果需要子线程完整执行,则需要阻塞主线程。

  • 1.2 Future接口

Future接口也是在JDK1.5中添加的,用于描述一个异步Callable任务的计算结果,如下所示。Future接口一共有5个方法:通过cancel()方法停止任务;通过isCanceld()方法判断任务是否被停止;通过isDone()方法检查任务是否完成;提供了两个重载版本的get方法,用于阻塞调用线程,直到任务完成或超时。

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只能通过阻塞或轮训的方式获取任务结果,阻塞违背异步变成的初衷,轮训的方式又耗费了CPU资源,通过Future获取异步任务结果的示里如下:

    @Test
    public void testFuture() throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        List<Future<Integer>> futures = new ArrayList<>();
        List<Integer> results = new ArrayList<>();
        for(int i=0; i <= 3; i++) {
            final int j = i;
            Future<Integer> future = executorService.submit(() -> {
                try {
                    Thread.sleep(j * 100L);
                } catch (InterruptedException e) {
                }
                return ThreadLocalRandom.current().nextInt(100);
            });
            futures.add(future);
        }
        for(Future<Integer> future : futures) {
            while(true) {
                if(future.isDone() && !future.isCancelled()) {
                    results.add(future.get());
                    break;
                }
            }
        }
        System.out.println(results);
    }
二、CompletableFuture

JDK8中添加了ComletableFuture类,它实现了Future接口和CompletionStage接口,其中CompletionStage可以看做是一个异步任务执行过程的抽象,如下。它简化了异步变成的复杂性,并提供了函数式变成的能力。ComletableFuture提供了同步和异步的执行方式,其异步方式又支持外部声明的线程池,如果不提供外部线程池,则异步时默认使用ForkJoinPool.commonPool()。

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
...
}

CompletableFuture实现了Future接口,因此,也可以通过以前的方式阻塞或轮训获取结果:

// 同Future
public T get() throws InterruptedException, ExecutionException {
...
}

// 同Future
public T get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException {
...
}

// 返回执行结果或抛出一个unchecked异常
public T join() {
...
}

// 如果执行结束则返回执行结果,否则返回valueIfAbsent
public T getNow(T valueIfAbsent) {
...
}

CompetableFuture.completedFuture是一个静态辅助类,用于返回一个计算好的CompletableFuture,示例:

    @Test
    public void testComletedFuture() throws InterruptedException, ExecutionException {
        CompletableFuture<String> future = CompletableFuture.completedFuture("result");
        System.out.println(future.get());
    }
---
result

通过以下四个静态方法为异步执行代码创建CompletableFuture对象:

// 接受Runnable任务,返回结果为空
public static CompletableFuture<Void> runAsync(Runnable runnable) {
...
}

// 接受Runnable任务,返回结果为空,指定线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {
...
}

// 接受Supplier对象,可指定返回类型
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
...
}

// 接受Supplier对象,可指定返回类型,指定线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) {
...
}

CompletableFuture任务执行示例如下,可指定执行线程池,如不指定则使用ForkJoinPool.commonPool()。

    @Test
    public void testCreateSyncTask() throws InterruptedException, ExecutionException {
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            return "supplyAsync result";
        });
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        CompletableFuture<Void> f2 = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());
        }, executorService);
        System.out.println(f1.get());
    }
---
ForkJoinPool.commonPool-worker-1
supplyAsync result
pool-1-thread-1

Future和CompletableFuture提供的get()方法是阻塞的,为了获取任务结果同时不阻塞当前线程,可使用CompletionStage提供的方法实现任务异步处理,有以下4中处理方式:

// 上游任务完成后在当前主线程中同步执行处理,向下传递上游处理结果或异常
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) {
...
}

// 上游任务完成后在当前子线程中异步执行处理,向下传递上游处理结果或异常
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) {
...
}

// 上游任务完成后在当前主线程中同步执行处理,指定线程池,向下传递上游处理结果或异常
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) {
...
}

// 上游任务异常时处理,在主线程中完成
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) {
...
}

示例代码如下:

    @Test
    public void testWhenComplete() throws InterruptedException, ExecutionException {
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
            return "test";
        }).whenCompleteAsync((result, exception) -> {
            if (result != null) {
                System.out.println(result);
            } else {
                exception.printStackTrace();
            }
        });
        System.out.println(f1.get());

        CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
            int i = 1 / 0;
            return "test";
        }).exceptionally((e) -> {
            e.printStackTrace();
            return "error";
        });
        System.out.println(f2.get());
    }
---
test
error
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
...

whenComplete方法入参是BiConsumer,它是一个纯消费者函数,不会修改返回值及其类型。下面一组接口除了whenComplete的功能外,同时具备转换结果的功能,通过参数BiFunction实现:

public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) {
...
}

public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) {
...
}

public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
...
}

示例代码如下:

    @Test
    public void testHandle() throws InterruptedException, ExecutionException {
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
            return "100";
        });
        CompletableFuture<Integer> f2 = f1.handleAsync((result, exception) -> {
            if(exception != null) {
                exception.printStackTrace();
            }
            return Integer.parseInt(result) * 10;
        });
        System.out.println(f2.get());
    }
---
1000

handle方法依然保留了对异常请款的处理,在BiFunction中指定其第二个参数类型是Throwble。接下来一组方法thenApply与handle类似,也可以对上游结果进行转换,同时忽略对异常情况的处理。

public <U> CompletableFuture<U> thenApplyFunction<? super T,? extends U> fn){
...
}

public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) {
...
}

public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) {
...
}

示例代码:

	@Test
    public void testThenApply() throws InterruptedException, ExecutionException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            //int i= 1/0;
            return "100";
        });
        CompletableFuture<Integer> f = future.thenApply(t -> Integer.parseInt(t) * 10).thenApply(t -> t * 10);
        System.out.println(f.get());
    }
---
10000

上述方法处理完成后,都会返回计算结果,CompletableFuture还提供了一组处理方法,只对上游处理结果进行消费,且没有返回,如果上游发生异常,则不执行该方法;同时,CompletableFuture还提供了一组加强版方法,提供两个CompletableFuture任务都完成或完成一个时执行的方法。

public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
...
}

public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
...
}

public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor) {
...
}

public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action) {
...
}

public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action) {
...
}

public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor) {
...
}
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) {
...
}

public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) {
...
}

public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor) {
...
}

示例代码:

    @Test
    public void testThenAccept() throws InterruptedException, ExecutionException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            int i = 1/0;
            return "1";
        });
        CompletableFuture<Void> f = future.thenAccept(t -> {
            System.out.println(t);
        });
    }
---

上述方法都依赖上游处理的结果,CompletableFuture还提供了一组方法,不依赖上游的结果,如下

public CompletableFuture<Void> thenRun(Runnable action) {
...
}

public CompletableFuture<Void> thenRunAsync(Runnable action) {
...
}

public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor) {
...
}

示例代码:

    @Test
    public void testThenRun() throws InterruptedException, ExecutionException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            return "result";
        });
        CompletableFuture<Void> f = future.thenRunAsync(() -> {
            System.out.println("future then run");
        });
    }
---
future then run

thenCompose这组方法与thenApply类似,都可以将上游执行结果作为本stage入参继续计算,并转换返回类型,不同的是thenCompose在一个新CompletableFuture对象执行计算。

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
...
}

public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) {
...
}

public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,Executor executor) {
...
}

示例代码:

    @Test
    public void testThenCompose() throws InterruptedException, ExecutionException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            return "10";
        });
        CompletableFuture<Integer> f = future.thenCompose(v -> {
            return CompletableFuture.supplyAsync(() -> {
               return Integer.parseInt(v) * 100;
            });
        });
        System.out.println(f.get());
    }
---
1000

thenCombine这组方法与thenAccepBoth类似,不同的是thenCombine有返回值:

public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) {
...
}

public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) {
...
}

public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor) {
...
}

示例代码:

    @Test
    public void testThenCombine() throws InterruptedException, ExecutionException {
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
            return "abc";
        });
        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
            return 123;
        });
        CompletableFuture<String> f3 = f1.thenCombine(f2, (m,n) -> {
            return m + " is " + n;
        });
        System.out.println(f3.get());
    }
---
abc is 123

最后两个方法用于组合多个CompletableFuture,allOf方法是当所有CompletableFuture执行完成后执行计算,anyOf方法是任意一个CompletableFuture执行完成后执行计算。

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
...
}

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
...
}

示例代码:

    @Test
    public void testAllof() throws InterruptedException, ExecutionException {
        List<CompletableFuture<Integer>> list = new ArrayList<>();
        Random random = new Random();
        for (int i=1; i < 10; i++) {
            list.add(CompletableFuture.supplyAsync(() -> {
                return random.nextInt(100);
            }));
        }
        CompletableFuture<Void> fn = CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()]));
        CompletableFuture<List<Integer>> fr = fn.thenApply(v -> {
            return list.stream().map(CompletableFuture::join).collect(Collectors.toList()); 
        });
        System.out.println(fr.get());
    }
---
[3, 97, 11, 93, 22, 77, 68, 26, 89]
参考
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值