首页 > 编程语言 >Java组合异步编程(1)

Java组合异步编程(1)

时间:2022-11-10 07:44:36浏览次数:54  
标签:异步 Java ... 编程 System CompletableFuture println out

您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~

 

在《计算机干活的两种方式》中我们提到过同步和异步的区别。所谓同步就是事情只能一件接一件地顺着干,而不能跳过。比如外卖小哥送外卖只能一件一件地送,不能说一件先送一半再送另一件。而异步就可以不按顺序出牌,但是这种不按顺序需要以互不影响为前提。比如李雷问韩梅梅一个数学问题,但是韩梅梅一时答不出来,李雷就先去干其他的事情去了。等韩梅梅知道答案的时候,再给李雷打电话告诉他结果。就像这样子:

 

 

 

 

Java也支持这种异步工作模式。还是以代码来举例。

public static void doSomethingElse() {
    try {
        System.out.println("doSomethingElse...");
        Thread.sleep(1_000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public static void executeComputation() {
    try {
        System.out.println("executeComputation...");
        Thread.sleep(2_000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// 使用Future
public static void testExecutor() throws InterruptedException, ExecutionException {
    // 申请线程池执行
    ExecutorService es = Executors.newSingleThreadExecutor();
    Future<Integer> f = es.submit(() -> {
        System.out.println(Thread.currentThread().getName());
        // 执行线程操作
        executeComputation();
        return 9527;
    });
    // 其他耗时操作
    doSomethingElse();
    System.out.println(f.get());
    es.shutdown();
}

 

 

运行代码后,也可以看到异步的执行痕迹。这可以用流程图来表示:

 

 

 

代码中的Futrure,就是那个可以通知李雷的「电话」。

 

那么问题来了,如果李雷有好几个不懂的问题,分别问了韩梅梅、赵梅梅、魏梅梅,而且需要答案之间有先后顺序怎么办呢?(比如要先得到韩梅梅的答案,再拿到赵梅梅的答案才有用)也就是对这种有多个「异步」任务的情况,该如何处理?这就是Java8中CompletableFuture(组合异步编程)要解决的问题。还是用代码来说话。

假如现在有三个异步任务,要求这三个异步任务能够「按顺序」执行,可以试着这么写:

ExecutorService service = Executors.newFixedThreadPool(3);
System.out.println("main start ...");
Future<Integer> f1 = service.submit(() -> {
    System.out.println(Thread.currentThread().getName());
    // 执行线程操作
    executeComputation();
    return 9525;
});
Future<Integer> f2 = service.submit(() -> {
    System.out.println(Thread.currentThread().getName());
    // 执行线程操作
    executeComputation();
    return 9526;
});
Future<Integer> f3 = service.submit(() -> {
    System.out.println(Thread.currentThread().getName());
    // 执行线程操作
    executeComputation();
    return 9527;
});
System.out.println("main end ...");
service.shutdown();

 

 

但是运行之后发现,无论如何,「System.out.println("main end ...");」这行代码都不会最后执行,这就达不到「按顺序」执行的要求了。

现在换成CompletableFuture试试看:

ExecutorService service = Executors.newFixedThreadPool(3);
        System.out.println("main start ...");
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("9525...");
            return 9525;
        }, service);

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("9526...");
            return 9526;
        }, service);

        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            System.out.println("9527...");
            return 9527;
        }, service);

        CompletableFuture<Void> future = CompletableFuture.allOf(future1, future2, future3);
        future.get();
        System.out.println("main end ...");
        service.shutdown();

 

 

运行后会发现9525、9526、9527三个任务虽然都是异步任务,但能够「按顺序」排列,满足了最初的要求。

 

现在,更进一步,如果某个答案取决于两个不相干的问题的共同回答,该怎么实现呢?例如,李雷问韩梅梅,然后又问了赵梅梅,只有在都问她们两个人的情况下,她们才会说出答案,否则不给答案。

// 两个CompletableFuture,全部任务执行完成才返回
public static void testAllOf() throws InterruptedException, ExecutionException {
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "world");

    CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2);
    // 返回值是void,不会合并结果
    System.out.println(combinedFuture.get());

    // 需要手动处理每个并行异步任务的结果
    String combined = Stream.of(future1, future2)
                            .map(CompletableFuture::join)
                            .collect(Collectors.joining(" "));
    // hello world
    System.out.println(combined);
}

 

 

这就是神奇的CompletableFuture组合异步编程。可以通过代码让不同的问题强制「同频」,这算不算乱点鸳鸯谱?

 

当然,如果有同学觉得这很过分,也可以允许「只要有问了她们其中之一就立刻给答案」:

// 两个CompletableFuture,只要有一个任务执行完成就返回
public static void testAnyOf() throws InterruptedException, ExecutionException {
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "world";
    });
    CompletableFuture<Object> combinedFuture = CompletableFuture.anyOf(future1, future2);
    // Hello World
    System.out.println(combinedFuture.get());
}

 

 

这更像唐伯虎点秋香中最后揭盖头的场景——只要揭了一个,剩下的全部自己揭开。

 

 


 

 

感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

标签:异步,Java,...,编程,System,CompletableFuture,println,out
From: https://www.cnblogs.com/xiangwang1111/p/16875136.html

相关文章

  • Java异常
    异常一、异常:就是程序出现不正常的情况。ThrowableErrorExceptionRuntimeException非RuntimeExceptionError:严重问题,不处理Exception:异常类,程序本身可......
  • java异步读取文件2种实现
    `importcom.sun.tools.jconsole.JConsoleContext;importjava.io.;importjava.lang.reflect.Array;importjava.math.BigDecimal;importjava.nio.ByteBuffer;impor......
  • C系统级编程-复习
    数组对象类型ArrayofType,它是多个相同对象类型的一维派生类型,包含两要素:元素个数,元素的对象类型所谓多维数组,不过是元素的迭代衍生,本质还是一维的声明对象标识......
  • [JavaScript-05]函数和箭头函数
    1.函数functionaddNums(num1=1,num2=2){console.log('num1+num2:',num1+num2);returnnum1+num2;}addNums();addNums(5,6);console.log('addNums(......
  • Java创建对象的5种方式
    new:最常见的方法Employeeemp1=newEmployee();newInstance()用newInstance()创建对象分2种1、使用class类的newInstance()方法需要有一个无参的构造方法,这个n......
  • Java集合简单介绍
    Java集合框架主要包括两种类型的容器,一种是Collection,存储一个元素集合,另一种是Map,存储键/值对映射。一、Collection集合List集合特点:有序可重复ArrayList集合(内部......
  • 关于Java中枚举Enum的深入剖析
    在编程语言中我们,都会接触到枚举类型,通常我们进行有穷的列举来实现一些限定。Java也不例外。Java中的枚举类型为Enum,本文将对枚举进行一些比较深入的剖析。什么是EnumEnum是......
  • JAVA-this关键字
    packagecom.itheima;/*学生类*/publicclassstudent02{//成员变量privateStringname;privateintage;publicvoidsetAge(intage){......
  • [JavaScript-03]IF 三元表达式 逻辑运算 == ===
    1.语句//if语句letx=10;if(x==10){console.log('xis10')};//ifelseifelsex=20;if(x===10){console.log('xis10');}elsei......
  • Java代码块运行顺序细节阐述
    以下这个例子非常好的阐述了父子类同时存在时静态代码块/静态变量初始化,普通代码块/普通成员变量初始化,构造器之间的具体运行顺序。注意,在构造器开头,我们可以看作按顺序隐......