JUC之CompletableFuture(一)

JUC之CompletableFuture(一)

大家好,我是栗子为。

“又有几天没和大家见面了,小为最近也是刚回到家,调整状态做好打工人的准备。虽然毕业了都说好好放松一下,但小为回到家每天都保证一定的学习时间,真的不是想做那个最卷的人,主要为了你们能学到更多(毫不夸张!)。

这个阶段呢我想带着大家学一下更深入的Java并发编程系列,从JUC(java.util.concurrent)包展开介绍,帮助大家在面试中轻松拿下这些高并发的问题。”

之前有介绍过Java多线程的相关知识,可以看Java多线程那些事(一)这篇文章,如果异步执行任务的时候需要有返回值,我们可以通过实现Callable接口,调用Executor的submit方法,再使用Future获取即可。当多个线程存在依赖组合的情况,我们能想到CountDownLatch、CyclicBarrier等,其实利用CompletableFuture就可以解决,这一次我们就来聊聊CompletableFuture。

话不多说,跟着小为开始今天的学习叭!



01


什么是Future接口?



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

有如下的方法

  • cancel()
  • get()
  • get(long, TimeUnit)
  • isCancelled()
  • isDone()

举个🌰

主线程让子线程去执行任务,此时主线程做自己的任务,过段时间再去获取子任务执行的结果



02


FutureTask实现类



三个特征:多线程有返回异步任务

构造方法:

  • FutureTask(Callablecallable)

  • FutureTask(Runnable runnable,V result)

举个🌰

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class FutureDemo {
  public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<String> futureTask = new FutureTask<>(new MyThread());
    Thread thread = new Thread(futureTask, "t1");
    thread.start();
    System.out.println(futureTask.get()); // 通过此方法拿到任务返回结果:call返回
  }
}

class MyThread implements Callable<String{

  @Override
  public String call() throws Exception {
    System.out.println("进入call方法");
    return "call返回";
  }
}


存在的问题

1.get()方法会阻塞

看如下代码(只看主函数)

public static void main(String[] args) throws ExecutionException, InterruptedException {
  FutureTask<String> futureTask = new FutureTask<>(() -> {
    System.out.println(Thread.currentThread().getName() + "t开始执行");
    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() + "t忙其他任务");
  System.out.println(futureTask.get());
}

运行结果如下

main 忙其他任务
t1 开始执行
(等待五秒钟...)
task over

若将futureTask.get()方法往上移,即

System.out.println(futureTask.get());
System.out.println(Thread.currentThread().getName() + "t忙其他任务");

运行结果如下

t1 开始执行
(等待五秒钟...)
task over
main 忙其他任务

可以发现get()方法拿到结果后才会继续往下执行,会阻塞主线程

2.isDone()轮询耗费CPU资源

采用轮询的方式,不断询问任务是否完成,这样能避免等待,其核心思想如下

while (true) {
  if (futureTask.isDone()) {
    System.out.println(futureTask.get());
    break;
  } else {
    try {
      TimeUnit.MILLISECONDS.sleep(500);
      System.out.println("请稍等正在处理中...");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

缺点:

  • 轮询的方式会耗费CPU资源,而且也不能及时得到任务结果

Future总结

  1. Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务结果
  2. Future对于一些简单业务场景比较友好
  3. 可以利用Future和线程池的方式来创建异步任务
  4. 可以用任务完成回调代替轮询,提高系统效率
  5. 若多个异步任务的结果相互依赖,利用Future则不能完成



03


CompletableFuture



在Java 8中, 新增加了一个包含50个方法左右的类: CompletableFuture,结合了Future的优点,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

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

在源码中对这个类的定义:

public class CompletableFuture<Timplements Future<T>, CompletionStage<T>{
  
}

CompletionStage接口

代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,有些类似Linux系统的管道分隔符

  • CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成后可能会触发另外一个阶段
  • 一个阶段的计算执行可以是一个Function,Consumer或者Runnable
  • 一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发

四大静态方法

JUC之CompletableFuture(一)
四大静态方法

注:

  1. 上述Executor executor参数,若没有指定Executor方法,直接使用默认的ForkJoinPool.commonPool()作为它的线程池执行异步代码;如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码
  2. Supplier是一个功能接口,因此可以用作lambda表达式或方法引用的赋值对象。

因此针对于不同的场景,采用不同的静态方法。下面来看一些具体场景

举个🌰(不指定Executor方法)

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class CompletableFutureDemo {
  public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
      System.out.println(Thread.currentThread().getName());
      try {
        TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
    System.out.println(completableFuture.get());
  }
}

结果如下

ForkJoinPool.commonPool-worker-9
null

举个🌰(指定Executor方法)

import java.util.concurrent.*;

public class CompletableFutureDemo {
  public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(3); // 创建一个固定大小的线程池
    CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
      System.out.println(Thread.currentThread().getName());
      try {
        TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }, threadPool);
    System.out.println(completableFuture.get());
    threadPool.shutdown();
  }
}

结果如下

pool-1-thread-1
null

可以发现,当我们指定线程池后,使用的是我们线程池里的线程

举个🌰(有返回值)

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class CompletableFutureDemo {
  public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
      System.out.println(Thread.currentThread().getName());
      try {
        TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "方法返回";
    });
    System.out.println(completableFuture.get());
  }
}

结果如下

ForkJoinPool.commonPool-worker-9
方法返回



04


总结



总结一下,并发编程包里的CompletableFuture类包含了Future方法的所有优点,也提供了更强大的API,解决了很多异步任务的场景,本篇主要介绍Future接口以及它的实现类FutureTask,让大家对异步编程,带返回值等有了初步的理解,下一讲将深入CompletableFuture类,看看它是如何解决FutureTask存在的阻塞以及轮询耗时等问题的。今天这篇文章写下来,小为还是觉得非常有必要,并发编程算是我们进入公司后一定要具备的技能,这其中有很多解决不同场景的类都需要我们去掌握,同时还能对以前学的知识进行复习和合并,所以大家一起加油哦!

关注六只栗子,面试不迷路!


作者    栗子为

编辑   一口栗子 

原文始发于微信公众号(六只栗子):JUC之CompletableFuture(一)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88589.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!