您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

Java8——异步编程

时间:2020-09-21 11:58:24  来源:  作者:
Java8——异步编程

 

创建任务并执行任务

无参创建

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

转入相应任务,无返回值

runAsync方法可以在后台执行异步计算,但是此时并没有返回值。持有一个Runnable对象。

CompletableFuture noReturn = CompletableFuture.runAsync(()->{
    //执行逻辑,无返回值
});

传入相应任务,有返回值

此时我们看到返回的是CompletableFuture<T>此处的T就是你想要的返回值的类型。其中的Supplier<T>是一个简单的函数式接口。

CompletableFuture<String> hasReturn = CompletableFuture.supplyAsync(new Supplier<String>() {
    @Override
    public String get() {
        return "hasReturn";
    }});

此时可以使用lambda表达式使上面的逻辑更加清晰

CompletableFuture<String> hasReturnLambda = CompletableFuture.supplyAsync(TestFuture::get);
private static String get() {
    return "hasReturnLambda";
}

获取返回值

异步任务也是有返回值的,当我们想要用到异步任务的返回值时,我们可以调用CompletableFuture的get()阻塞,直到有异步任务执行完有返回值才往下执行。

我们将上面的get()方法改造一下,使其停顿十秒时间。

private static String get() {
    System.out.println("Begin Invoke getFuntureHasReturnLambda");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
    }    System.out.println("End Invoke getFuntureHasReturnLambda");
    return "hasReturnLambda";
}

然后进行调用

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<String> funtureHasReturnLambda = (CompletableFuture<String>) getFuntureHasReturnLambda();    System.out.println("Main Method Is Invoking");
    funtureHasReturnLambda.get();
    System.out.println("Main Method End");
}

可以看到输出如下,只有调用get()方法的时候才会阻塞当前线程。

Main Method Is Invoking
Begin Invoke getFuntureHasReturnLambdaEnd Invoke getFuntureHasReturnLambdaMain Method End

自定义返回值

除了等待异步任务返回值以外,我们也可以在任意时候调用complete()方法来自定义返回值。

CompletableFuture<String> funtureHasReturnLambda = (CompletableFuture<String>) getFuntureHasReturnLambda();
System.out.println("Main Method Is Invoking");
new Thread(()->{
    System.out.println("Thread Is Invoking ");
    try {
        Thread.sleep(1000);
        funtureHasReturnLambda.complete("custome value");
    } catch (InterruptedException e) {
        e.printStackTrace();    }    System.out.println("Thread End ");
}).run();String value = funtureHasReturnLambda.get();System.out.println("Main Method End value is "+ value);

我们可以发现输出是新起线程的输出值,当然这是因为我们的异步方法设置了等待10秒,如果此时异步方法等待1秒,新起的线程等待10秒,那么输出的值就是异步方法中的值了。

Main Method Is Invoking
Begin Invoke getFuntureHasReturnLambdaThread Is Invoking Thread End 
Main Method End value is custome value

按顺序执行异步任务

如果有一个异步任务的完成需要依赖前一个异步任务的完成,那么该如何写呢?是调用get()方法获得返回值以后然后再执行吗?这样写有些麻烦,CompletableFuture为我们提供了方法来完成我们想要顺序执行一些异步任务的需求。thenApply、thenAccept、thenRun这三个方法。这三个方法的区别就是。

方法名是否可获得前一个任务的返回值是否有返回值thenApply能获得有thenAccept能获得无thenRun不可获得无

所以一般来说thenAccept、thenRun这两个方法在调用链的最末端使用。接下来我们用真实的例子感受一下。

//thenApply  可获取到前一个任务的返回值,也有返回值
CompletableFuture<String> seqFutureOne = CompletableFuture.supplyAsync(()-> "seqFutureOne");
CompletableFuture<String> seqFutureTwo = seqFutureOne.thenApply(name -> name + " seqFutureTwo");
System.out.println(seqFutureTwo.get());//thenAccept  可获取到前一个任务的返回值,但是无返回值
CompletableFuture<Void> thenAccept = seqFutureOne        .thenAccept(name -> System.out.println(name + "thenAccept"));
System.out.println("-------------");
System.out.println(thenAccept.get());//thenRun 获取不到前一个任务的返回值,也无返回值
System.out.println("-------------");
CompletableFuture<Void> thenRun = seqFutureOne.thenRun(() -> {
    System.out.println("thenRun");
});System.out.println(thenRun.get());

返回的信息如下

seqFutureOne seqFutureTwo
seqFuture.NEThenAccept-------------null
-------------thenRunnull

thenApply和thenApplyAsync的区别

我们可以发现这三个方法都带有一个后缀为Async的方法,例如thenApplyAsync。那么带Async的方法和不带此后缀的方法有什么不同呢?我们就以thenApply和thenApplyAsync两个方法进行对比,其他的和这个一样的。

这两个方法区别就在于谁去执行这个任务,如果使用thenApplyAsync,那么执行的线程是从ForkJoinPool.commonPool()中获取不同的线程进行执行,如果使用thenApply,如果supplyAsync方法执行速度特别快,那么thenApply任务就是主线程进行执行,如果执行特别慢的话就是和supplyAsync执行线程一样。接下来我们通过例子来看一下,使用sleep方法来反应supplyAsync执行速度的快慢。

//thenApply和thenApplyAsync的区别
System.out.println("-------------");
CompletableFuture<String> supplyAsyncWithSleep = CompletableFuture.supplyAsync(()->{
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();    }    return "supplyAsyncWithSleep Thread Id : " + Thread.currentThread();
});CompletableFuture<String> thenApply = supplyAsyncWithSleep        .thenApply(name -> name + "------thenApply Thread Id : " + Thread.currentThread());
CompletableFuture<String> thenApplyAsync = supplyAsyncWithSleep        .thenApplyAsync(name -> name + "------thenApplyAsync Thread Id : " + Thread.currentThread());
System.out.println("Main Thread Id: "+ Thread.currentThread());
System.out.println(thenApply.get());System.out.println(thenApplyAsync.get());System.out.println("-------------No Sleep");
CompletableFuture<String> supplyAsyncNoSleep = CompletableFuture.supplyAsync(()->{
    return "supplyAsyncNoSleep Thread Id : " + Thread.currentThread();
});CompletableFuture<String> thenApplyNoSleep = supplyAsyncNoSleep        .thenApply(name -> name + "------thenApply Thread Id : " + Thread.currentThread());
CompletableFuture<String> thenApplyAsyncNoSleep = supplyAsyncNoSleep        .thenApplyAsync(name -> name + "------thenApplyAsync Thread Id : " + Thread.currentThread());
System.out.println("Main Thread Id: "+ Thread.currentThread());
System.out.println(thenApplyNoSleep.get());System.out.println(thenApplyAsyncNoSleep.get());

我们可以看到输出位

-------------
Main Thread Id: Thread[main,5,main]
supplyAsyncWithSleep Thread Id : Thread[ForkJoinPool.commonPool-worker-1,5,main]------thenApply Thread Id : Thread[ForkJoinPool.commonPool-worker-1,5,main]
supplyAsyncWithSleep Thread Id : Thread[ForkJoinPool.commonPool-worker-1,5,main]------thenApplyAsync Thread Id : Thread[ForkJoinPool.commonPool-worker-1,5,main]
-------------No Sleep
Main Thread Id: Thread[main,5,main]
supplyAsyncNoSleep Thread Id : Thread[ForkJoinPool.commonPool-worker-2,5,main]------thenApply Thread Id : Thread[main,5,main]
supplyAsyncNoSleep Thread Id : Thread[ForkJoinPool.commonPool-worker-2,5,main]------thenApplyAsync Thread Id : Thread[ForkJoinPool.commonPool-worker-2,5,main]

可以看到supplyAsync方法执行速度慢的话thenApply方法执行线程和supplyAsync执行线程相同,如果supplyAsync方法执行速度快的话,那么thenApply方法执行线程和Main方法执行线程相同。

组合CompletableFuture

将两个CompletableFuture组合到一起有两个方法

  1. thenCompose():当第一个任务完成时才会执行第二个操作
  2. thenCombine():两个异步任务全部完成时才会执行某些操作

thenCompose() 用法

我们定义两个异步任务,假设第二个定时任务需要用到第一个定时任务的返回值。

public static CompletableFuture<String> getTastOne(){
    return CompletableFuture.supplyAsync(()-> "topOne");
}public static CompletableFuture<String> getTastTwo(String s){
    return CompletableFuture.supplyAsync(()-> s + "  topTwo");
}

我们利用thenCompose()方法进行编写

CompletableFuture<String> thenComposeComplet = getTastOne().thenCompose(s -> getTastTwo(s));
System.out.println(thenComposeComplet.get());

输出就是

topOne  topTwo

如果还记得前面的thenApply()方法的话,应该会想这个利用thenApply()方法也是能够实现类似的功能的。

//thenApply
CompletableFuture<CompletableFuture<String>> thenApply = getTastOne()
        .thenApply(s -> getTastTwo(s));
System.out.println(thenApply.get().get());

但是我们发现返回值是嵌套返回的一个类型,而想要获得最终的返回值需要调用两次get()

thenCombine() 用法

例如我们此时需要计算两个异步方法返回值的和。求和这个操作是必须是两个异步方法得出来值的情况下才能进行计算,因此我们可以用thenCombine()方法进行计算。

CompletableFuture<Integer> thenComposeone = CompletableFuture.supplyAsync(() -> 192);
CompletableFuture<Integer> thenComposeTwo = CompletableFuture.supplyAsync(() -> 196);
CompletableFuture<Integer> thenComposeCount = thenComposeOne        .thenCombine(thenComposeTwo, (s, y) -> s + y);
System.out.println(thenComposeCount.get());

此时thenComposeOne和thenComposeTwo都完成时才会调用传给thenCombine方法的回调函数。

组合多个CompletableFuture

在上面我们用thenCompose()和thenCombine()两个方法将两个CompletableFuture组装起来,如果我们想要将任意数量的CompletableFuture组合起来呢?可以使用下面两个方法进行组合。

  • allOf():等待所有CompletableFuture完后以后才会运行回调函数
  • anyOf():只要其中一个CompletableFuture完成,那么就会执行回调函数。注意此时其他的任务也就不执行了。

接下来演示一下两个方法的用法

//allOf()
CompletableFuture<Integer> one = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> two = CompletableFuture.supplyAsync(() -> 2);
CompletableFuture<Integer> three = CompletableFuture.supplyAsync(() -> 3);
CompletableFuture<Integer> four = CompletableFuture.supplyAsync(() -> 4);
CompletableFuture<Integer> five = CompletableFuture.supplyAsync(() -> 5);
CompletableFuture<Integer> six = CompletableFuture.supplyAsync(() -> 6);
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(one, two, three, four, five, six);voidCompletableFuture.thenApply(v->{    return Stream.of(one,two,three,four, five, six)
            .map(CompletableFuture::join)            .collect(Collectors.toList());}).thenAccept(System.out::println);CompletableFuture<Void> voidCompletableFuture1 = CompletableFuture.runAsync(() -> {
    try {
        Thread.sleep(1000);
    } catch (Exception e) {
    }    System.out.println("1");
});

我们定义了6个CompletableFuture等待所有的CompletableFuture等待所有任务完成以后然后将其值输出。

anyOf()的用法

CompletableFuture<Void> voidCompletableFuture1 = CompletableFuture.runAsync(() -> {
try {
    Thread.sleep(1000);
} catch (Exception e) {
}System.out.println("voidCompletableFuture1");
});CompletableFuture<Void> voidCompletableFutur2 = CompletableFuture.runAsync(() -> {
try {
    Thread.sleep(2000);
} catch (Exception e) {
}System.out.println("voidCompletableFutur2");
});CompletableFuture<Void> voidCompletableFuture3 = CompletableFuture.runAsync(() -> {
try {
    Thread.sleep(3000);
} catch (Exception e) {
}System.out.println("voidCompletableFuture3");
});CompletableFuture<Object> objectCompletableFuture = CompletableFuture    .anyOf(voidCompletableFuture1, voidCompletableFutur2, voidCompletableFuture3);objectCompletableFuture.get();

这里我们定义了3个CompletableFuture进行一些耗时的任务,此时第一个CompletableFuture会率先完成。打印结果如下。

voidCompletableFuture1

异常处理

我们了解了CompletableFuture如何异步执行,如何组合不同的CompletableFuture,如何顺序执行CompletableFuture。那么接下来还有一个重要的一步,就是在执行异步任务时发生异常的话该怎么办。我们先写个例子。

CompletableFuture.supplyAsync(()->{
    //发生异常
    int i = 10/0;
    return "Success";
}).thenRun(()-> System.out.println("thenRun"))
.thenAccept(v -> System.out.println("thenAccept"));
CompletableFuture.runAsync(()-> System.out.println("CompletableFuture.runAsync"));

执行结果为,我们发现只要执行链中有一个发生了异常,那么接下来的链条也就不执行了,但是主流程下的其他CompletableFuture还是会运行的。

CompletableFuture.runAsync

exceptionally()

我们可以使用exceptionally进行异常的处理

//处理异常
CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> {
    //发生异常
    int i = 10 / 0;
    return "Success";
}).exceptionally(e -> {    System.out.println(e);    return "Exception has Handl";
});System.out.println(exceptionally.get());

打印如下,可以发现其接收值是异常信息,也能够返回自定义返回值。

JAVA.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
Exception has Handl

handle()

调用handle()方法也能够捕捉到异常并且自定义返回值,他和exceptionally()方法不同一点是handle()方法无论发没发生异常都会被调用。例子如下

System.out.println("-------有异常-------");
CompletableFuture.supplyAsync(()->{
    //发生异常
    int i = 10/0;
    return "Success";
}).handle((response,e)->{
    System.out.println("Exception:" + e);
    System.out.println("Response:" + response);
    return response;
});System.out.println("-------无异常-------");
CompletableFuture.supplyAsync(()->{
    return "Sucess";
}).handle((response,e)->{
    System.out.println("Exception:" + e);
    System.out.println("Response:" + response);
    return response;
});

打印如下,我们可以看到在没有发生异常的时候handle()方法也被调用了

-------有异常-------
Exception:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
Response:null
-------无异常-------
Exception:null
Response:Sucess


Tags:Java8   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
ParallelStream并行流在之前文章Java8新特性-Stream API中有简单的介绍过它的使用。如Collection集合可以通过parallelStream()的得到一个并行流。Stream<Integer> stream =...【详细内容】
2022-05-09  Tags: Java8  点击:(71)  评论:(0)  加入收藏
public class LambdaDemo { public static void main(String[] args) { /** * 用来判定true或者false boolean test(T t); */ Predicat...【详细内容】
2021-10-18  Tags: Java8  点击:(192)  评论:(0)  加入收藏
IDEA在手 天下我有我们打算将Pondus的所有生产服务器升级到这一新版本。 从那时起,我们将大部分代码库迁移到lambda表达式、数据流和新的日期API上。 我们也会使用Nashorn来...【详细内容】
2021-06-10  Tags: Java8  点击:(249)  评论:(0)  加入收藏
为什么会新增这样一个string辅助类?原有的stringbuilder太死板,不支持分割,如果想让最终的字符串以逗号隔开,需要这样写StringBuilder sb = new StringBuilder();IntStream.ran...【详细内容】
2021-05-17  Tags: Java8  点击:(243)  评论:(0)  加入收藏
Lambda表达式简介Lambda表达式是java 1.8才开始有的重要功能,使用Lambda表达式可以替代匿名内部类,代码简洁易懂,提升工作效率。上代码:使用内部类和使用lambda实现代码量对比函...【详细内容】
2021-03-15  Tags: Java8  点击:(442)  评论:(0)  加入收藏
创建任务并执行任务无参创建 CompletableFuture<String> noArgsFuture = new CompletableFuture<>();转入相应任务,无返回值runAsync方法可以在后台执行异步计算,但是此时并...【详细内容】
2020-09-21  Tags: Java8  点击:(90)  评论:(0)  加入收藏
Optional类是Java 8新增的一个类,用以解决程序中常见的NullPointerException异常问题。本篇文章将详细介绍Optional类,以及如何用它消除代码中的null检查。 避免使用null检查...【详细内容】
2020-07-30  Tags: Java8  点击:(38)  评论:(0)  加入收藏
Java8 parallelStream并发安全背景Java8的stream接口极大地减少了for循环写法的复杂性,stream提供了map/reduce/collect等一系列聚合接口,还支持并发操作:parallelStream。在爬...【详细内容】
2020-07-27  Tags: Java8  点击:(421)  评论:(0)  加入收藏
前言最近公司里比较新的项目里面,看到了很多关于java8新特性的用法,由于之前自己对java8的新特性不是很了解也没有去做深入研究,所以最近就系统的去学习了一下,然后总结了一篇文...【详细内容】
2020-07-18  Tags: Java8  点击:(61)  评论:(0)  加入收藏
Java 8 推出了全新的日期时间API,在教程中我们将通过一些简单的实例来学习如何使用新API。Java处理日期、日历和时间的方式一直为社区所诟病,将 java.util.Date设定为可变类型...【详细内容】
2020-03-30  Tags: Java8  点击:(59)  评论:(0)  加入收藏
▌简易百科推荐
本文的目的是演示一个专门设计用于对收据照片执行OCR(光学字符识别)操作并自动从中提取关键业务信息的 API,例如企业名称和地址、电话号码、收据总额等等。在页面的下方,我提供...【详细内容】
2022-07-15  qaseven    Tags: OCR   点击:(2)  评论:(0)  加入收藏
StackOverflowError 可能会让Java开发人员感到恼火,因为它是我们可能遇到的最常见的运行时错误之一。在本文中,我们将通过查看各种代码示例以及如何处理它来了解此错误是如何...【详细内容】
2022-07-14  java程序猿    Tags:StackOverflowError   点击:(4)  评论:(0)  加入收藏
写在前面无意中看到ch1ng师傅的文章觉得很有趣,不得不感叹师傅太厉害了,但我一看那长篇的函数总觉得会有更骚的东西,所幸还真的有,借此机会就发出来一探究竟,同时也不得不感慨下R...【详细内容】
2022-07-13  江江111  今日头条  Tags:waf 绕过   点击:(4)  评论:(0)  加入收藏
今早,看到CSDN里推荐的Python获取女朋友发来加班拍照定位地址是酒店的段子,本来准备验证下,顺便练练手的,最后,安装执行pip install json报没有指定版本号。一怒之下搞我大JAVA,验...【详细内容】
2022-07-12  java小悠    Tags:Java 技术   点击:(8)  评论:(0)  加入收藏
核心代码:import java.util.Map;import java.util.List;import java.util.Iterator;import java.util.ArrayList;import java.io.IOException;import java.io.InputStream;im...【详细内容】
2022-07-12  编程课堂    Tags:Java Post   点击:(10)  评论:(0)  加入收藏
上半年春招的时候,作为面试官,对于面试表现的不错的同学会要求其写一小段代码看看。题目很简单:给定一个日期,然后计算下距离今天相差的天数。本以为这么个问题就是用来活跃面试...【详细内容】
2022-07-11  架构悟道    Tags:JAVA   点击:(11)  评论:(0)  加入收藏
跳表是一种数据结构,用于借助连接到元素子序列的链表层次结构来存储元素的排序列表。跳表允许以有效的方式处理项目查找。跳表是一种概率数据结构,这意味着它跳过整个列表中的...【详细内容】
2022-07-08  java程序猿    Tags:跳表   点击:(13)  评论:(0)  加入收藏
概述背景是不是在实际开发工作当中经常碰到自己写的代码在开发、测试环境行云流水稳得一笔,可一到线上就经常不是缺这个就是少那个反正就是一顿报错抽风似的,线上调试代码又很...【详细内容】
2022-07-08  程序猿的自述    Tags:Arthas   点击:(16)  评论:(0)  加入收藏
我之前分享了Java和Go语言版本的gRPC接口的服务端和客户端的开发,使用的基本都是基础的原声API,旧文如下: Grpc服务开发和接口测试初探【Java】 2022-04-20 gRPC服务开发和接口...【详细内容】
2022-07-07  FunTester    Tags:gRPC   点击:(16)  评论:(0)  加入收藏
Linux下启动Java程序的脚本程序startup.sh#!/bin/bashnohup java -Djava.security.egd=file:/dev/./urandom -Xms512m -Xmx512m -XX:MetaspaceSize=128M -XX:MaxMetaspaceSi...【详细内容】
2022-07-07  精科研习    Tags:Java   点击:(23)  评论:(0)  加入收藏
站内最新
站内热门
站内头条