线程 CompletableFuture详解

CompletableFuture

参考:https://blog.youkuaiyun.com/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(都有结果了往下走)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值