线程 CompletableFuture详解

CompletableFuture

参考:https://blog.csdn.net/u012129558/article/details/78962759
https://www.jianshu.com/p/bdc6bd50f7d2

Future模式的缺点
  • Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成。
  • 要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作。要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。
CompletableFuture介绍

Java 8新增的CompletableFuture类正是吸收了所有Google Guava中ListenableFuture和SettableFuture的特征,还提供了其它强大的功能,让Java拥有了完整的非阻塞编程模型:FuturePromiseCallback(在Java8之前,只有无Callback 的Future)。

CompletableFuture弥补了Future模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。

1.创建对象

CompletableFuture有两类最基本的创建对象的静态方法:

CompletableFuture的静态工厂方法
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);
public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);

runAsyncsupplyAsync 方法的区别是runAsync返回的CompletableFuture是没有返回值的。

		CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName()+"hello!");
        });

        try {
            Void aVoid = future.get();
            System.out.println(aVoid); //null
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
ForkJoinPool.commonPool-worker-1hello!
null
        ExecutorService executor = Executors.newFixedThreadPool(3);

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "world!",executor);

        try {
            System.out.println(future.get()); //world!
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


2.获取对象内容

CompletableFuture有几个相关方法,包括:

public T get() throws InterruptedException, ExecutionException;
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
public T join();
public T getNow(T valueIfAbsent);
  • get 方法会使当前线程阻塞,并且等待直到 future 完成,并且将返回 future 的值
  • 带参数的 get 方法需要传入对应的时间长度,一旦超出这个时间,会抛出TimeoutException
  • join 方法与 get 并无太大差别,但 join 方法不能被打断
  • getNow 方法不会阻塞线程,而是立即返回值,如果该 future 当前没有完成,则会立刻返回该方法的传入参数.
  • get 和 join 方法会在正常的情况下返回值,在遇到异常的情况下将异常抛出

说明:通常情况下,为了使所有的多线程都执行完后,再进行下一步处理(如return等),可以使用join。虽然join可以获取结果,但这里是为了使用其阻塞的作用。

1.不阻塞将不会等待结果
		ExecutorService executor = Executors.newFixedThreadPool(3);
        List<String> list = Lists.newArrayList("hi","didi","hello");
        List<String> nList = Lists.newArrayList();
        list.forEach(s->CompletableFuture.runAsync(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            nList.add(s.toUpperCase());
        }, executor));

        System.out.println(nList);//[]

上述由于异步处理时需要花费1s时间,故nList为空。可以等待1s以后,再查看nList的内容:

        ExecutorService executor = Executors.newFixedThreadPool(3);
        List<String> list = Lists.newArrayList("hi","didi","hello");
        List<String> nList = Lists.newArrayList();
        list.forEach(s->CompletableFuture.runAsync(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            nList.add(s.toUpperCase());
        }, executor));

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

        System.out.println(nList);//[HI, DIDI, HELLO]

上述等待一定时间后,nList可能已经有了全部结果,可能结果不是很全。通过等待时间不是好的做法,我们可以通过join来等待多线程运行结束。

2.join等待多线程运行结束
        ExecutorService executor = Executors.newFixedThreadPool(3);
        List<String> list = Lists.newArrayList("hi","didi","hello");
        List<String> nList = Lists.newArrayList();
        list.forEach(s->CompletableFuture.runAsync(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            nList.add(s.toUpperCase());
        }, executor).join());

        System.out.println(nList);//[HI, DIDI, HELLO]

join将等待所有线程执行完后,执行下一步。

thread1        thread2           thread3
      \            |            /
			    	join(都有结果了往下走)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CompletableFuture是Java8引入的一个异步编程工具类,用于简化异步编程,提高代码可读性和可维护性。它可以让你在一个线程中进行异步编程,而无需显式地创建线程或使用回调函数。 CompletableFuture可以看作是对Future的扩展,它支持链式调用和组合操作,可以将多个CompletableFuture组合在一起,形成一个复杂的异步操作流程。它也提供了一些方法,比如thenApply、thenAccept、thenRun等,用于处理异步操作的结果。 在使用CompletableFuture时,我们可以将异步操作放在一个CompletableFuture对象中,然后通过链式调用的方式,将多个异步操作组合在一起,形成一个异步操作流程。当所有的异步操作都执行完毕后,可以使用CompletableFuture的get方法,获取异步操作的结果。 下面是一个使用CompletableFuture的示例代码: ```java CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { // 异步操作 return 10; }).thenApplyAsync(result -> { // 处理异步操作结果 return result * 2; }).thenApplyAsync(result -> { // 处理异步操作结果 return result + 3; }); // 获取异步操作的结果 Integer result = future.get(); ``` 在上面的代码中,我们首先使用CompletableFuture的supplyAsync方法,将异步操作放在一个CompletableFuture对象中。然后通过thenApplyAsync方法,将两个处理异步操作结果的函数串联在一起,形成一个异步操作流程。最后通过get方法,获取异步操作的结果。 除了thenApplyAsync方法,CompletableFuture还提供了许多其他的方法,比如thenAcceptAsync、thenRunAsync、thenComposeAsync等,可以根据具体需求来选择使用。 总之,CompletableFuture是一个非常强大的异步编程工具类,可以让你写出更加简洁、易读、易维护的异步代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值