【JUC】Future + CompletableFuture详解

文章目录

  • [1. Future接口](#1. Future接口)
    • [1.1 Future 的理论知识](#1.1 Future 的理论知识)
    • [1.2 Future接口的三个特点](#1.2 Future接口的三个特点)
    • [1.3 关于Future存在的问题](#1.3 关于Future存在的问题)
    • [1.4 Future的架构](#1.4 Future的架构)
    • [1.5 Future编码实战和优缺点分析](#1.5 Future编码实战和优缺点分析)
    • [1.6 Future的使用举例](#1.6 Future的使用举例)
  • [2.Future 到 CompletableFuture 的过渡](#2.Future 到 CompletableFuture 的过渡)
  • 3.CompletableFuture接口
    • [3.1 CompletableFuture为什么会出现](#3.1 CompletableFuture为什么会出现)
    • [3.2 CompletableFuture架构](#3.2 CompletableFuture架构)
    • [3.3 CompletableFuture的类特点](#3.3 CompletableFuture的类特点)
    • [3.4 核心的四个静态方法,来创建一个异步任务](#3.4 核心的四个静态方法,来创建一个异步任务)
    • [3.5 CompletableFuture的四个静态方法的使用](#3.5 CompletableFuture的四个静态方法的使用)
    • [3.6 CompletableFuture的优点](#3.6 CompletableFuture的优点)
    • CompletableFuture常用方法

1. Future接口

1.1 Future 的理论知识

Future接口(实现类:FutureTask)定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消异步任务的执行、判断任务是否被取消、判断任务执行是否完毕等。

举例:比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开始执行任务后,主线程就去做其他事情了,忙完其他事情或者先执行完,过了一会再才去获取子任务的执行结果或变更的任务状态(老师上课时间想喝水,他继续讲课不结束上课这个主线程,让学生去小卖部帮老师买水完成这个耗时和费力的任务)。

复制代码
public interface Future<V> {
//取消异步任务的执行
boolean cancel(boolean mayInterruptIfRunning);
//判断异步任务是否被取消
boolean isCancelled();
//判断任务是否被执行完毕
boolean isDone();
//获取到异步任务的结果
V get() throws InterruptedException, ExecutionException;
//只在timeout时间内等待获取异步任务的结果
V get(long timeout, TimeUnit unit)
       throws InterruptedException, ExecutionException, TimeoutException;
}

1.2 Future接口的三个特点

  1. 多线程
  2. 有返回值
  3. 异步任务

1.3 关于Future存在的问题

  • 现在 Thread (Runnable target,String name ) 可以满足特点1
  • 有返回值则要使用callable,但是初始化Thread不可以传参Callable,只能传参Runnable
  • 异步任务则需要实现future接口

1.4 Future的架构

这里要通过Future的架构来理解Future是如何解决这些问题的

  • FutureTask作为Future的实现类,继承了RunnableFuture接口,而RunnableFuture接口又继承了FutureRunnable接口;在FutureTask,即Future的落地实现类中,重写了run()方法,并在这个重写的run()方法中调用了callable方法,这样便可以解决返回值的问题
  • RunnableFuture 接口的定义可以看出,它继承了 Runnable 接口, 那么这样,就可以将 FutureTask 类以构造方法参数的形式传递给Thread了。
  • RunnableFuture 接口中有一个 run 方法,那么这就要求实现 RunnableFuture 接口的类要去实现了 run() 方法。这样,FutureTask 类既然实现了 RunnableFuture 接口,那么 FutureTask 类中必然有一个 run 方法是供 Thread 类调用的。

源码解读:

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

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

● 结论: Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果

1.6 Future的使用举例

复制代码
public class FutureApiDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        FutureTask<String> futureTask = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() + "--------come in");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task over";
        });

        Thread t1 = new Thread(futureTask, "t1");
        t1.start();
        System.out.println(Thread.currentThread().getName() + " ------忙其他任务");
        while (true) {//轮询
            if(futureTask.isDone()){
                System.out.println(futureTask.get());
                break;
            }else{
                TimeUnit.MILLISECONDS.sleep(500);
                System.out.println("正在处理中,不要催了,越催越慢");
            }
        }
    }
}

2.Future 到 CompletableFuture 的过渡

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

回调通知:

○ 应对Future的完成时间,完成了可以告诉我,也就是我们的回调通知

○ 通过轮询 的方式去判断任务是否完成这样非常占cpu并且代码也不优雅

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

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

○ 想将多个异步任务的结果组合起来,后一个异步任务的计算结果需要钱一个异步任务的值

○ 想将两个或多个异步计算合并成为一个异步计算,这几个异步计算互相独立,同时后面这个又依赖前一个处理的结果

● 对计算速度选最快的:

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

● 结论:

○ 使用Future之前提供的API功能太少,处理起来不够优雅,这时候还是让CompletableFuture以声明式的方式优雅的处理这些需求。

3.CompletableFuture接口

3.1 CompletableFuture为什么会出现

get()方法在Future计算完成之前会一直处在阻塞状态下 ,阻塞的方式和异步编程的设计理念相违背。

isDone()方法容易耗费cpu资源(cpu空转),

● 对于真正的异步处理我们希望是可以通过传入回调函数,在Future结束时自动调用该回调函数 ,这样,我们就不用等待结果
jdk8设计出CompletableFutureCompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方

3.2 CompletableFuture架构

● 接口CompletionStage:

○ 代表异步计算 过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段。

○ 一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发。

● 类CompletableFuture

○ 提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果 ,也提供了转换和组合CompletableFuture的方法

○ 它可能代表一个明确完成的Future,也可能代表一个完成阶段(CompletionStage),它支持在计算完成以后触发一些函数或执行某些动作

3.3 CompletableFuture的类特点

  1. 分阶段链式执行方法,上一个阶段的结果可以传递给下一个阶段。
  2. 主线程不需要阻塞或轮询等待结果 ,而是交给CompletableFuture获取结果。整个过程正常完成和异常完成情况,CompletableFuture可以分别处理。
  3. 多个CompletableFuture可以取先执行的结果或者取合并后的结果。

3.4 核心的四个静态方法,来创建一个异步任务

对于上述Executor参数说明:若没有指定,则使用默认的ForkJoinPoolcommonPool()作为它的线程池执行异步代码,如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码。

3.5 CompletableFuture的四个静态方法的使用

  • runAsync 无返回值

    public static CompletableFuture<Void> runAsync(Runnable runnable)
    public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)

    public class CompletableFutureBuildDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(3);

    复制代码
          CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
              System.out.println(Thread.currentThread().getName());
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          },executorService);
    
          System.out.println(completableFuture.get()); //null
    
    
          CompletableFuture<String> objectCompletableFuture = CompletableFuture.supplyAsync(()->{
              System.out.println(Thread.currentThread().getName());
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              return "hello supplyAsync";
          },executorService);
    
          System.out.println(objectCompletableFuture.get());//hello supplyAsync
    
          executorService.shutdown();
    
      }

    }

运行结果

  • supplyAsync 有返回值

    public static CompletableFuture supplyAsync(Supplier supplier)
    public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)

    public class CompletableFutureUseDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName() + "---come in");
    int result = ThreadLocalRandom.current().nextInt(10);
    try {
    TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    if (result > 5) { //模拟产生异常情况
    int i = 10 / 0;
    }
    System.out.println("----------1秒钟后出结果" + result);
    return result;
    }, executorService).whenComplete((v, e) -> {
    if (e == null) {
    System.out.println("计算完成 更新系统" + v);
    }
    }).exceptionally(e -> {
    e.printStackTrace();
    System.out.println("异常情况:" + e.getCause() + " " + e.getMessage());
    return null;
    });
    System.out.println(Thread.currentThread().getName() + "先去完成其他任务");
    executorService.shutdown();
    }
    }

运行结果:

3.6 CompletableFuture的优点

CompletableFuture优点:

● 异步任务结束 时,会自动回调某个对象的方法

● 主线程设置好回调后 ,不用关心异步任务的执行,异步任务之间可以顺序执行

● 异步任务出错 时,会自动回调某个对象的方法

CompletableFuture常用方法

● 获得结果和触发计算

○ 获取结果

public T get()

public T get(long timeout,TimeUnit unit)

public T join()get 一样的作用,只是不需要抛出异常

public T getNow(T valuelfAbsent) :计算完成就返回正常值,否则返回备用值(传入的参数),立即获取结果不阻塞

○ 主动触发计算

public boolean complete(T value) :是否打断get方法立即返回括号值

● 对计算结果进行处理

thenApply:计算结果存在依赖关系,这两个线程串行化,由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停

handle: 计算结果存在依赖关系,这两个线程串行化,有异常也可以往下走一步

相关推荐
C灿灿数模3 小时前
2025MathorCup大数据竞赛A题B题选题建议与分析,思路模型
1024程序员节
木法星人3 小时前
Ubuntu安装nvm(无需梯子自动连接github下载安装)
ubuntu·nvm·1024程序员节
君鼎3 小时前
C++通用业务标准库中常用接口函数总结
c++·1024程序员节
杨筱毅3 小时前
【穿越Effective C++】条款5:了解C++默默编写并调用哪些函数——编译器自动生成的秘密
c++·effective c++·1024程序员节
代码不停3 小时前
网络 UDP 和 TCP / IP详细介绍
网络·网络协议·tcp/ip·udp·1024程序员节
Bert丶seven3 小时前
鸿蒙Harmony实战开发教学(No.8)-Hyperlink超链接组件基础到进阶篇
华为·harmonyos·arkts·arkui·1024程序员节·开发教程
虚行3 小时前
1.Go基础知识入门
1024程序员节
禁默3 小时前
浙人医创新开新篇——用KingbaseES数据库开创首个多院区异构多活容灾架构
国产数据库·1024程序员节·金仓数据库·kingbasees数据库
乐十九3 小时前
单例模式:从基础实现到高级应用
1024程序员节