JUC并发编程 异步编程利器CompletableFuture 介绍

1 从FutureTask到CompletableFuture

1.1 Future

Future接口(FutureTask实现类)定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消异步任务的执行、判断任务是否被取消、判断任务执行是否完毕等。
举例:比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开始执行任务后,主线程就去做其他事情了,忙完其他事情或者先执行完,过了一会再才去获取子任务的执行结果或变更的任务状态(老师上课时间想喝水,他继续讲课不结束上课这个主线程,让学生去小卖部帮老师买水完成这个耗时和费力的任务)。

1.2 Future接口常用实现类FutureTask异步任务

1.2.1 Future接口能干什么

Future是Java5新加的一个接口,它提供一种异步并行计算的功能,如果主线程需要执行一个很耗时的计算任务,我们会就可以通过Future把这个任务放进异步线程中执行,主线程继续处理其他任务或者先行结束,再通过Future获取计算结果。

1.2.2 Future接口相关架构

UML图

public class CompletableFutureDemo {

    @SneakyThrows
    public static void main(String[] args) {

        FutureTask<String> futureTask = new FutureTask<>(new MyThread());

        Thread thread = new Thread(futureTask);
        thread.start();

        System.out.println(futureTask.get());

    }

}

class MyThread implements Callable<String >{

    @Override
    public String call() throws Exception {

        System.out.println("----come in call()");

        return "hello Callable";
    }
}

1.2.3 Future编码实战和优缺点分析

● 优点:Future+线程池异步多线程任务配合,能显著提高程序的运行效率。
● 缺点:
○ get()阻塞---一旦调用get()方法求结果,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,如果没有计算完成容易程序堵塞。
○ isDone()轮询---轮询的方式会耗费无谓的cpu资源,而且也不见得能及时得到计算结果,如果想要异步获取结果,通常会以轮询的方式去获取结果,尽量不要阻塞。
● 结论:Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果。

1.2.3.1 get()阻塞演示
package com.kwfruit.thread.step01;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

@Slf4j
public class FutureAPIDemo {
    
    @SneakyThrows
    public static void main(String[] args) {
        
        FutureTask<String> futureTask = new FutureTask<>(()->{
            log.debug("[{}] ------come in",Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(3);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            return "task over";
        });
        
        Thread t1   = new Thread(futureTask);
        t1.start();
         /**
         * 获取异步任务返回的结果值
         * 必须拿到返回结果才能往下执行
         */
        log.debug("futureTask 结果值:{}",futureTask.get());
        

      //System.out.println(futureTask.get(3,TimeUnit.SECONDS));
        System.out.println("嘻嘻嘻");
    }
    
    
}

结论:一旦调用get()方法求结果,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,如果没有计算完成容易程序堵塞

1.2.3.2 isDone()轮询

上面介绍了 用FutureTask get()方法获取返回值 会造成程序阻塞,如果线程没有执行完,没有返回结果,会一直等待下去。下面我们用isDone()方法配合get()方法进行优化,用打印日志的方式监控程序的执行状态。

package com.kwfruit.thread.step01;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

@Slf4j
public class FutureAPIDemo {
    
    @SneakyThrows
    public static void main(String[] args) {
        
        FutureTask<String> futureTask = new FutureTask<>(()->{
            log.debug("[{}] ------come in",Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(3);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            return "task over";
        });
        
        Thread t1   = new Thread(futureTask);
        t1.start();


        while (true){
           if (futureTask.isDone()){
               log.error("futureTask 的值为:[{}]",futureTask.get());
              break;
           }else{
               try {
                   TimeUnit.MILLISECONDS.sleep(300);
               }catch (InterruptedException e){
                   e.printStackTrace();
               }
               log.error("等待程序执行中......");
           }
       }
    }  
}


结论:轮询的方式会耗费无谓的cpu资源,而且也不见得能及时得到计算结果,如果想要异步获取结果,通常会以轮询的方式去获取结果,尽量不要阻塞

1.3 总结

1.3.1 对于简单的业务场景使用Future完全ok

1.3.2 回调通知:

应对Future的完成时间,完成了可以告诉我,也就是我们的回调通知
通过轮询的方式去判断任务是否完成这样非常占cpu并且代码也不优雅

1.3.3 创建异步任务:Future+线程池组合

1.3.4 多个任务前后依赖可以组合处理(水煮鱼--->买鱼--->调料--->下锅):

想将多个异步任务的结果组合起来,后一个异步任务的计算结果需要钱一个异步任务的值
想将两个或多个异步计算合并成为一个异步计算,这几个异步计算互相独立,同时后面这个又依赖前一个处理的结果

1.3.5 对计算速度选最快的:

当Future集合中某个任务最快结束时,返回结果,返回第一名处理结果

1.3.6 结论:

使用Future之前提供的那点API就囊中羞涩,处理起来不够优雅,这时候还是让CompletableFuture以声明式的方式优雅的处理这些需求。
从i到i++
Future能干的,CompletableFuture都能干

posted @ 2024-01-14 12:43  KwFruit  阅读(34)  评论(0编辑  收藏  举报