【Java并发编程】CompletableFuture的基础理论

Future接口理论知识复习

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

在这里插入图片描述

只要是异步线程,比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动了子线程开始执行任务后,主线程就去做其他事情了,忙其他事情或者先执行完,过了一会去获取子任务的执行结果或变更的任务状态。

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

Future接口能干什么

是JAVA5新加的一个接口,他提供了一种异步并行计算的功能。

如果主线程需要执行一个很耗时的计算任务,我们就可以通过future把这个任务放到异步线程中执行。

主线程继续处理其他任务或者先行结束,再通过Future获取计算结果。

相关架构

首先看一下Runnable接口和Callable接口

   class MyThread1 implements Runnable{
    
    

        @Override
        public void run() {
    
    

        }
    }
    
    class MyThread2 implements Callable<String>{
    
    

        @Override
        public String call() throws Exception {
    
    
            return null;
        }
    }

在这里插入图片描述

Runnable接口有一个子类 -> RunnableFuture 其继承了Runnable和Future接口。所以它可以实现多线程和异步

RunnableFuture 又有一个子类FutureTask类,其可以传入Callable接口。

它实现了RunnableFuture 。

在这里插入图片描述

可以达到多线程、异步和有返回值。

编码实战和优缺点分析

测试:

运行Callable接口

public class CallableTest {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        FutureTask<String> stringFutureTask = new FutureTask<>(new MyThread1(),"结果");
        new Thread(stringFutureTask).start();
        String s = stringFutureTask.get();
        System.out.println(s);//输出返回值
    }

    /**
     * Runnable 接口
     */
  static   class MyThread1 implements Runnable{
    
    

        @Override
        public void run() {
    
    
            System.out.println("runable 接口");
        }
    }

    /**
     * Callable接口
     */
   static class MyThread2 implements Callable<String>{
    
    

        @Override
        public String call() throws Exception {
    
    
            System.out.println("callable接口");
            return "返回值";
        }
    }


}


输出结果

在这里插入图片描述

运行Runnable接口:

 public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        FutureTask<String> stringFutureTask = new FutureTask<>(new MyThread1(),"结果");
        new Thread(stringFutureTask).start();
        String s = stringFutureTask.get();
        System.out.println(s);//输出返回值
    }

结果:

在这里插入图片描述

优点

future+线程池异步多线程任务配合,能显著提高程序的执行效率

缺点

get()阻塞,因为需要得到返回值。

一但调用get()方法求结果,如果计算没有完成容易导致程序阻塞。

public class FutureThreadPoolDemo {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        FutureTask<String> futureTask = new FutureTask<>(()->{
    
    
            System.out.println(Thread.currentThread().getName() + "/t----开始");
            TimeUnit.SECONDS.sleep(5);
            return "运行结束";
        });

        Thread t1 = new Thread(futureTask,"t1");
        t1.start();
        String s = futureTask.get();
        System.out.println(s);
        System.out.println("main\t 结束");
    }
}

结果:

在这里插入图片描述

因为调用了get()方法,所以会一直等待返回结果在继续运行。

也可以设置等待时间,如果到达时间没有得到返回结果,就会抛出异常。

String s = futureTask.get(3,TimeUnit.SECONDS);

在这里插入图片描述

可以捕获异常、进行下一步操作。

可以使用 isDone()方法 可以判断任务是否完成。

public class FutureThreadPoolDemo {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
    
    
        FutureTask<String> futureTask = new FutureTask<>(()->{
    
    
            System.out.println(Thread.currentThread().getName() + "/t----开始");
            TimeUnit.SECONDS.sleep(5);
            return "运行结束";
        });

        Thread t1 = new Thread(futureTask,"t1");
        t1.start();
        while (true){
    
    
            if (futureTask.isDone()){
    
    
                System.out.println(futureTask.get());
                break;
            }else {
    
    
                TimeUnit.MILLISECONDS.sleep(500);
                System.out.println("正在处理");
            }
        }
    }
}

结果:

在这里插入图片描述

会一直轮询、会耗费CPU资源。

如果想要异步获取结果,通常都会以轮询的方式去获取结果尽量不要阻塞

Future对结果的获取都不是很友好

CompletableFuture对Future的改进

CompletableFuture为什么出现

get()方法容易阻塞,isDome()方法容易消耗CPU资源

CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。

CompletableFuture和CompletionStage源码分别介绍

在这里插入图片描述

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

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

有点类似管道通信

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

他可能代表一个完成的阶段,也可能代表一个阶段。

核心的四个静态方法、来创建一个异步线程

可以使用构造方法创建一个对象

CompletableFuture<String> stringCompletableFuture = new CompletableFuture<>();

但是不推荐使用无参构造获得。(API说创建一个未完成的对象)

可通过下面的方式来创建对象

  1. runAsync 无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executorexecutor)

如果没有指定线程池,则使用默认的ForkJoinPool.commonPool作为默认线程池。

public class CompletableFutureDemo {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Void> CompletableFutureTest = CompletableFuture.runAsync(() -> {
    
    
            System.out.println(Thread.currentThread().getName());

            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        });

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

    }
}

在这里插入图片描述

如传入一个线程池

public class CompletableFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Executor executorService = Executors.newSingleThreadExecutor();
        CompletableFuture<Void> CompletableFutureTest = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());

            ;
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },executorService);

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

    }
}

在这里插入图片描述

  1. supplyAsunc 有返回值
 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)

单参,即不传入线程池

CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            return "hello supplyAsync";
        });

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

在这里插入图片描述

传入线程池

ExecutorService executorService = Executors.newFixedThreadPool(10);
        CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            return "hello supplyAsync";
        },executorService);

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

在这里插入图片描述

通用功能,如何减少阻塞和轮询

从Java8开始引入了CompletableFure,他是Fature的功能增强办,减少轮询和阻塞,可以传入回调参数,当异步任务完成或者发生异常时,自动调用回调对象的回调功能。

CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println(Thread.currentThread().getName());
            int result = ThreadLocalRandom.current().nextInt(10);
            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
            System.out.println("1s后结果为" + result);
            return result;
        });

        System.out.println(Thread.currentThread().getName()+"主线程取完成其他任务");
        System.out.println(completableFuture.get());

在这里插入图片描述

完全可以取代Futrue接口

在这里插入图片描述

两个参数。第一个参数是消费对象,第二个是异常对象

如以下程序

public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println(Thread.currentThread().getName());
            int result = ThreadLocalRandom.current().nextInt(10);
            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
            System.out.println("1s后结果为" + result);
            return result;
        }).whenComplete((v,e)->{
    
    
            if (e == null){
    
    
                System.out.println("计算完成"+v);
            }
        }).exceptionally(e->{
    
    
            e.printStackTrace();
            return null;
        });
        
        System.out.println(Thread.currentThread().getName()+"主线程取完成其他任务");

    }

结果:

在这里插入图片描述

并没有输出结果,这是为什么?

这是因为默认线程池就相当于一个守护线程,main方法结束了就死亡了。所以可以让主线程等待几秒。也可以传入一个线程池。

public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        try {
    
    
            CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
    
    
                System.out.println(Thread.currentThread().getName());
                int result = ThreadLocalRandom.current().nextInt(10);
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
                System.out.println("1s后结果为" + result);
                return result;
            },executorService).whenComplete((v,e)->{
    
    
                if (e == null){
    
    
                    System.out.println("计算完成"+v);
                }
            }).exceptionally(e->{
    
    
                e.printStackTrace();
                return null;
            });

            System.out.println(Thread.currentThread().getName()+"主线程取完成其他任务");
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            executorService.shutdown();
        }


    }

结果:

在这里插入图片描述

CompletableFuture的优点:

  • 异步任务结束时,会自动回调某个对象的方法
  • 主线程设置好回调后,不再关心异步任务的执行,异步任务之间可以顺序执行
  • 异步任务出错时,会自动回

猜你喜欢

转载自blog.csdn.net/qq_45795744/article/details/125361179
今日推荐