iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > VUE >如何理解异步编程的Future
  • 942
分享到

如何理解异步编程的Future

2024-04-02 19:04:59 942人浏览 八月长安
摘要

本篇内容介绍了“如何理解异步编程的Future”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!先聊聊线程池的

本篇内容介绍了“如何理解异步编程的Future”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

先聊聊线程池的提交方式

谈到 Future 的时候,我们基本上就会想到线程池,想到它的几种提交方式。

先是最简单的,execute 方式提交,不关心返回值的,直接往线程池里面扔任务就完事:

public class jdkThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));         //execute(Runnable command)方法。没有返回值         executor.execute(() -> {             System.out.println("关注why技术");         });         Thread.currentThread().join();     } }

可以看一下 execute 方法,接受一个 Runnable 方法,返回类型是 void:

如何理解异步编程的Future

然后是 submit 方法。你知道线程池有几种 submit 方法吗?

虽然你经常用,但是可能你从来没有关心过人家。呸,渣男:

如何理解异步编程的Future

有三种 submit。这三种按照提交任务的类型来算分为两个类型。

  • 提交执行 Runnable 类型的任务。

  • 提交执行 Callable 类型的任务。

但是返回值都是 Future,这才是我们关心的东西。

也许你知道线程池有三种 submit 方法,但是也许你根本不知道里面的任务分为两种类型,你就只知道往线程池里面扔,也不管扔的是什么类型的任务。

我们先看一下 Callable 类型的任务是怎么执行的:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));         Future<String> future = executor.submit(() -> {             System.out.println("关注why技术");             return "这次一定!";         });         System.out.println("future的内容:" + future.get());         Thread.currentThread().join();     } }

这里利用 lambda 表达式,直接在任务体里面带上一个返回值,这时你看调用的方法就变成了这个:

如何理解异步编程的Future

运行结果也能拿到任务体里面的返回了。输出结果如下:

如何理解异步编程的Future

好,接下来再说说 submit 的任务为 Runable 类型的情况。

这个时候有两个重载的形式:

如何理解异步编程的Future

标号为 ① 的方法扔进去一个 Runable 的任务,返回一个 Future,而这个返回的 Future  ,相当于是返回了一个寂寞。下面我会说到原因。

标号为 ② 的方法扔进去一个 Runable 的任务的同时,再扔进去一个泛型 T ,而巧好返回的 Future 里面的泛型也是  T,那么我们大胆的猜测一下这就是同一个对象。如果是同一个对象,说明我们可以一个对象传到任务体里面去一顿操作,然后通过 Future  再次拿到这个对象的。一会就去验证。

来,先验证标号为 ① 的方法,我为啥说它返回了一个寂寞。

首先,还是先把测试案例放在这里:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));         Future<?> future = executor.submit(() -> {             System.out.println("关注why技术");         });         System.out.println("future的内容:" + future.get());         Thread.currentThread().join();     } }

可以看到,确实是调用的标号为 ① 的方法:

如何理解异步编程的Future

同时,我们也可以看到 future.get() 方法的返回值为 null。

你说,这不是返回了一个寂寞是干啥?

当你想用标号为 ① 的方法时,我劝你直接用 execute 方式提交任务。还不需要构建一个寂寞的返回值,徒增无用对象。

接下来,我们看看标号为 ② 的方法是怎么用的:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));         AtomicInteger atomicInteger = new AtomicInteger();         Future<AtomicInteger> future = executor.submit(() -> {             System.out.println("关注why技术");             //在这里进行计算逻辑             atomicInteger.set(5201314);         }, atomicInteger);          System.out.println("future的内容:" + future.get());         Thread.currentThread().join();     } }

可以看到改造之后,确实是调用了标号为 ② 的方法:

如何理解异步编程的Future

future.get() 方法的输出值也是异步任务中我们经过计算后得出的 5201314。

你看,渣男就是这样,明明不懂你,还非得用甜言蜜语来轰炸你。呸。

好了。综上,线程池的提交方式一共有四种:一种 execute,无返回值。三种 submit,有返回值。

submit 中按照提交任务的类型又分为两种:一个是 Callable,一个是 Runable。

submit 中 Runable 的任务类型又有两个重载方法:一个返回了个寂寞,一个返回了个渣男。哦,不。一个返回了个寂寞,一个返回了个对象。

这个时候就有人要站出来说:你说的不对,你就是瞎说,明明就只有 execute 这一种提交方式。

是的,“只有 execute 这一种提交方式”这一种说法也是没错的。

请看源码

如何理解异步编程的Future

三种 submit 方法里面调用的都是 execute 方法。

能把前面这些方法娓娓道来,从表面谈到内在的这种人,才是好人。

如何理解异步编程的Future

只有爱你,才会把你研究透。

当然,还有这几种提交方式,用的不多,就不展开说了:

如何理解异步编程的Future

好,上面这些东西捋清楚了之后。我们再聚焦到返回值 Future 上:

从上面的代码我们可以看出,当我们想要返回值的时候,都需要调用下面的这个 get() 方法:

如何理解异步编程的Future

而从这个方法的描述可以看出,这是一个阻塞方法。拿不到值就在那里等着。当然,还有一个带超时时间的 get 方法,等指定时间后就不等了。

呸,渣男。没耐心,这点时间都舍不得等。

总之就是有可能要等的。只要等,那么就是阻塞。只要是阻塞,就是一个假异步。

所以总结一下这种场景下返回的 Future 的不足之处:

  • 只有主动调用 get 方法去获取值,但是有可能值还没准备好,就阻塞等待。

  • 任务处理过程中出现异常会把异常隐藏,封装到 Future 里面去,只有调用 get 方法的时候才知道异常了。

写到这里的时候我不禁想起一个形象的例子,我给你举一个。

假设你想约你的女神一起去吃饭。女神嘛,肯定是要先画个美美的妆才会出去逛街的。而女神化妆就可以类比为我们提交的一个异步任务。

假设你是一个小屌丝,那么女神就会对你说:我已经开始化妆了,你到楼下了就给我打电话。

然后你就收拾行头准备出发,这就是你提交异步任务后还可以做一些自己的事情。

你花了一小时到了女神楼下,打电话给她:女神你好,我到你楼下了。

女神说:你先等着吧,我的妆还没画好呢。

于是你开始等待,无尽的等待。这就是不带超时时间的 future.get() 方法。

如何理解异步编程的Future

也有可能你硬气一点,对女神说:我最多再等 24 小时哈,超过 24 小时不下楼,我就走了。

这就是带超时时间的 future.get(timeout,unit) 方法:

如何理解异步编程的Future

结果 24 小时之后,女神还没下来,你就走了。

当然,还有一种情况就是你到楼下给女神打电话,女神说:哎,今天我男神约我出去看电影,就不和你去吃饭了哈。本来我想提前给你说的,但是我又记不起你电话,只有你打过来我才能告诉你。就这样,你自己玩去吧。

这就相当于异步任务执行过程中抛出了异常,而你只有在调用了 get 方法(打电话操作)之后才知道原来异常了。

而真正的异步是你不用等我,我好了我就叫你。

就像女神接到男神的电话时说的:我需要一点时间准备一下,你先玩自己的吧,我一会好了给你打电话。

这让我想起了好莱坞原则:Don't Call Us,We'll Call you!

接下来,让我们见识一下真正的异步。

什么叫真正的:“你先玩自己的,我一会好了叫你。”

Guava 的 Future

女神说的:“好了叫你”。

就是一种回调机制。说到回调,那么我们就需要在异步任务提交之后,注册一个回调函数就行。

Google 提供的 Guava 包里面对 JDK 的 Future 进行了扩展:

如何理解异步编程的Future

新增了一个 addListenter 方法,入参是一个 Runnable 的任务类型和一个线程池。

使用方法,先看代码:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());         ListenableFuture<String> listenableFuture = executor.submit(() -> {             System.out.println(Thread.currentThread().getName()+"-女神:我开始化妆了,好了我叫你。");             TimeUnit.SECONDS.sleep(5);             return "化妆完毕了。";         });          listenableFuture.addListener(() -> {             try {                 System.out.println(Thread.currentThread().getName()+"-future的内容:" + listenableFuture.get());             } catch (Exception e) {                 e.printStackTrace();             }         }, executor);         System.out.println(Thread.currentThread().getName()+"-等女神化妆的时候可以干点自己的事情。");         Thread.currentThread().join();     } }

首先创建线程池的方式变了,需要用 Guava 里面的 MoreExecutors 方法装饰一下:

ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());

然后用装饰后的 executor 调用 submit 方法(任意一种),就会返回 ListenableFuture ,拿到这个  ListenableFuture 之后,我们就可以在上面注册监听:

如何理解异步编程的Future

所以,上面的程序我们调用的是入参为 callable 类型的接口:

如何理解异步编程的Future

从运行结果可以看出来:获取运行结果是在另外的线程里面执行的,完全没有阻塞主线程。

和之前的“假异步”还是有很大区别的。

除了上面的 addListener 方法外,其实我更喜欢用 FutureCallback 的方式。

如何理解异步编程的Future

可以看一下代码,非常的直观:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());         ListenableFuture<String> listenableFuture = executor.submit(() -> {             System.out.println(Thread.currentThread().getName()+"-女神:我开始化妆了,好了我叫你。");             TimeUnit.SECONDS.sleep(5);             return "化妆完毕了。";         });         Futures.addCallback(listenableFuture, new FutureCallback<String>() {             @Override             public void onSuccess(@Nullable String result) {                 System.out.println(Thread.currentThread().getName()+"-future的内容:" + result);             }              @Override             public void onFailure(Throwable t) {                 System.out.println(Thread.currentThread().getName()+"-女神放你鸽子了。");                 t.printStackTrace();             }         });         System.out.println(Thread.currentThread().getName()+"-等女神化妆的时候可以干点自己的事情。");         Thread.currentThread().join();     } }

有 onSuccess 方法和 onFailure 方法。

上面的程序输出结果为:

如何理解异步编程的Future

如果异步任务执行的时候抛出了异常,比如女神被她的男神约走了,异步任务改成这样:

ListenableFuture<String> listenableFuture = executor.submit(() -> {             System.out.println(Thread.currentThread().getName() + "-女神:我开始化妆了,好了我叫你。");             TimeUnit.SECONDS.sleep(5);             throw new Exception("男神约我看电影,就不和你吃饭了。");         });

最终的运行结果就是这样:

如何理解异步编程的Future

是的,女神去看电影了。她一定只是不想吃饭而已。

加强版的Future - CompletableFuture

第一小节讲的 Future 是 JDK 1.5 时代的产物:

如何理解异步编程的Future

经过了这么多年的发展,Doug Lea 在 JDK 1.8 里面引入了新的 CompletableFuture :

如何理解异步编程的Future

到了 JDK 1.8 时代,这才是真正的异步编程。

CompletableFuture 实现了两个接口,一个是我们熟悉的 Future ,一个是 CompletionStage。

CompletionStage接口,你看这个接口的名称中有一个 Stage :

如何理解异步编程的Future

可以把这个接口理解为一个任务的某个阶段。所以多个 CompletionStage  链接在一起就是一个任务链。前一个任务完成后,下一个任务就会自动触发。

CompletableFuture 里面的方法非常的多。

由于篇幅原因,我就只演示一个方法:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {             System.out.println(Thread.currentThread().getName() + "-女神:我开始化妆了,好了我叫你。");             try {                 TimeUnit.SECONDS.sleep(5);             } catch (InterruptedException e) {                 e.printStackTrace();             }             return "化妆完毕了。";         });          completableFuture.whenComplete((returnStr, exception) -> {             if (exception == null) {                 System.out.println(Thread.currentThread().getName() + returnStr);             } else {                 System.out.println(Thread.currentThread().getName() + "女神放你鸽子了。");                 exception.printStackTrace();             }         });         System.out.println(Thread.currentThread().getName() + "-等女神化妆的时候可以干点自己的事情。");         Thread.currentThread().join();     } }

该方法的执行结果如下:

如何理解异步编程的Future

我们执行的时候并没有指定用什么线程池,但是从结果可以看到也是异步的执行。

从输出日志中是可以看出端倪的,ForkJoinPool.commonPool() 是其默认使用的线程池。

如何理解异步编程的Future

当然,我们也可以自己指定。

如何理解异步编程的Future

这个方法在很多开源框架里面使用的还是非常的多的。

接下来主要看看 CompletableFuture 对于异常的处理。我觉得非常的优雅。

不需要 try-catch 代码块包裹,也不需要调用 Future.get() 才知道异常了,它提供了一个 handle  方法,可以处理上游异步任务中出现的异常:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         CompletableFuture.supplyAsync(() -> {             System.out.println(Thread.currentThread().getName() + "-女神:我开始化妆了,好了我叫你。");             throw new RuntimeException("男神约我看电影了,我们下次再约吧,你是个好人。");         }).handleAsync((result, exception) -> {             if (exception != null) {                 System.out.println(Thread.currentThread().getName() + "-女神放你鸽子了!");                 return exception.getCause();             } else {                 return result;             }         }).thenApplyAsync((returnStr) -> {             System.out.println(Thread.currentThread().getName() + "-" + returnStr);             return returnStr;         });         System.out.println(Thread.currentThread().getName() + "-等女神化妆的时候可以干点自己的事情。");         Thread.currentThread().join();     } }

由于女神在化妆的时候,接到男神的电话约她看电影,就只能放你鸽子了。

所以,上面程序的输出结果如下:

如何理解异步编程的Future

如果,你顺利把女神约出来了,是这样的:

如何理解异步编程的Future

“如何理解异步编程的Future”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: 如何理解异步编程的Future

本文链接: https://www.lsjlt.com/news/84748.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何理解异步编程的Future
    本篇内容介绍了“如何理解异步编程的Future”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!先聊聊线程池的...
    99+
    2024-04-02
  • Java异步编程工具Twitter Future详解
    目录异步编程(Twitter Future)为啥要异步基本用法1、封装计算逻辑,异步返回。2、异步计算结果串联异步处理3、并行多个异步任务,统一等待结果4、异步错误处理Twitter...
    99+
    2024-04-02
  • Flutter Future异步如何创建
    本篇内容介绍了“Flutter Future异步如何创建”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!异步futureFuture...
    99+
    2023-07-05
  • 并发编程 | 从Future到CompletableFuture - 简化 Java 中的异步编程
    引言 在并发编程中,我们经常需要处理多线程的任务,这些任务往往具有依赖性,异步性,且需要在所有任务完成后获取结果。Java 8 引入了 CompletableFuture 类,它带来了一种新的编程模式,让我们能够以函数式编程的方式处理并发任...
    99+
    2023-08-19
    java 后端
  • 如何理解Java 8异步编程CompletableFuture
    本篇内容介绍了“如何理解Java 8异步编程CompletableFuture”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!本文大纲速看一、...
    99+
    2023-06-15
  • 并发编程之如何理解Future&FutureTask
    本篇内容介绍了“并发编程之如何理解Future&FutureTask”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读...
    99+
    2024-04-02
  • Laravel异步编程:如何利用PHP异步编程处理文件上传?
    Laravel是一个流行的PHP框架,它提供了许多强大的功能和工具,可以帮助开发人员更轻松地构建高质量的Web应用程序。其中一个重要的功能是异步编程。在本文中,我们将探讨如何利用PHP异步编程处理文件上传。 传统的文件上传方式是同步的,这...
    99+
    2023-10-05
    异步编程 文件 laravel
  • 如何理解JS的同步异步编程和EventLoop底层机制
    这篇文章主要讲解了“如何理解JS的同步异步编程和EventLoop底层机制”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解JS的同步异步编程和Even...
    99+
    2024-04-02
  • 怎么理解异步编程RxJava
    这篇文章给大家介绍怎么理解异步编程RxJava,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。前言前段时间写了一篇对协程的一些理解,里面提到了不管是协程还是callback,本质上其实提供的是一种异步无阻塞的编程模式;并...
    99+
    2023-06-17
  • 基于事件的异步编程模式EMP如何理解
    基于事件的异步编程模式EMP如何理解,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。.NET1.0中基于IAsyncResult设计模式的异步编程模型(APM),它使用Sys...
    99+
    2023-06-17
  • 详解SpringBoot如何开启异步编程
    目录一、什么是异步?二、为什么要使用异步编程?三、SpringBoot开启异步编程1、有返回值的异步方法2、@Async使用的线程池3、SpringBoot使用的默认线程池源码解析4...
    99+
    2023-05-17
    SpringBoot异步编程 SpringBoot异步
  • Java 异步编程中,如何实现路径响应的异步处理?
    在现代的Web应用程序中,异步处理是非常重要的。这是因为在Web应用程序中,对于某些请求,可能需要进行较长时间的处理,例如处理大量数据或与其他服务进行通信。如果应用程序在这些请求上花费太长时间,将会给用户带来糟糕的体验。因此,异步处理可以...
    99+
    2023-10-31
    异步编程 响应 path
  • Python中的同步与异步编程,如何处理数组?
    随着互联网的飞速发展,计算机的性能越来越强大,人们的需求也越来越高。在这样的背景下,同步和异步编程逐渐成为了热门话题。同步和异步编程的本质区别在于程序的执行方式不同,同步是指程序在执行某个任务时,会一直等待任务完成后再执行下一个任务;而异...
    99+
    2023-09-08
    同步 数组 异步编程
  • Java 异步编程教程:如何在http请求中实现异步编程?
    随着互联网技术的不断发展,越来越多的应用需要处理大量的网络请求。而在高并发场景下,同步的网络请求会导致线程阻塞,从而降低应用的性能。因此,采用异步编程模式可以有效地提高应用的性能和并发能力。本文将为你介绍如何在Java中实现异步编程,并通...
    99+
    2023-06-25
    异步编程 教程 http
  • Laravel文件异步编程:如何通过PHP异步编程提高文件处理效率?
    Laravel是一款流行的PHP框架,它提供了许多有用的功能,其中包括文件处理。但是,处理大型文件时,传统的同步文件处理方式可能会导致性能问题。为了解决这个问题,我们可以使用异步编程来提高文件处理效率。在本文中,我们将学习如何使用Larav...
    99+
    2023-10-05
    异步编程 文件 laravel
  • ASP如何实现异步编程?
    ASP如何实现异步编程? 随着互联网技术的不断发展,人们对网站的要求也越来越高。网站需要更快的响应速度,更好的用户体验。为了满足这些要求,异步编程逐渐被引入到网站开发中。ASP是一种广泛使用的Web开发技术,那么ASP如何实现异步编程呢? ...
    99+
    2023-10-16
    异步编程 linux numpy
  • SpringBoot如何实现异步编程
    目录为什么要用异步框架,它解决什么问题?SpringBoot如何实现异步调用?实现异步调用为什么要给@Async自定义线程池?多个线程池处理配置默认线程池小结首先我们来看看在Spri...
    99+
    2024-04-02
  • Laravel和JavaScript的异步编程与Python的异步编程有何不同?
    随着计算机技术的不断发展,异步编程已经成为了现代编程中的一个非常重要的概念。许多编程语言都提供了异步编程的支持,如Laravel、JavaScript和Python等。虽然它们都可以实现异步编程,但是它们之间的实现方式有所不同。本文将探讨...
    99+
    2023-09-08
    异步编程 laravel javascript
  • Go语言、Git、JavaScript:如何处理异步编程?
    异步编程是现代编程中非常重要的一个概念,它能够提高程序的运行效率和性能。在这篇文章中,我们将介绍如何使用Go语言、Git和JavaScript处理异步编程。 一、Go语言中的异步编程 Go语言是一种并发性很强的编程语言,它有很多内置的机制可...
    99+
    2023-11-04
    git javascript 异步编程
  • ASP中如何实现异步编程?
    在现代Web开发中,异步编程已经成为一种必不可少的技术,因为它可以提高应用程序的性能和可伸缩性。在ASP中,异步编程可以通过多种方式来实现,包括使用异步页、使用XMLHTTP对象和使用异步Web服务。本文将介绍这些方法并提供相应的演示代码...
    99+
    2023-08-04
    存储 异步编程 npm
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作