• java 多线程 合并多个查询结果


    场景:假如你突然想做饭,但是没有厨具,也没有食材。网上购买厨具比较方便,食材去超市买更放心。

    实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材。所以,在主线程里面另起一个子线程去网购厨具。

    但是,子线程执行的结果是要返回厨具的,而run方法是没有返回值的。所以,这才是难点,需要好好考虑一下。

    模拟代码:

     1 package test;
     2 
     3 import java.util.concurrent.Callable;
     4 import java.util.concurrent.ExecutionException;
     5 import java.util.concurrent.FutureTask;
     6 
     7 public class FutureCook {
     8 
     9     public static void main(String[] args) throws InterruptedException, ExecutionException {
    10         long startTime = System.currentTimeMillis();
    11         // 第一步 网购厨具
    12         Callable<Chuju> onlineShopping = new Callable<Chuju>() {
    13 
    14             @Override
    15             public Chuju call() throws Exception {
    16                 System.out.println("第一步:下单");
    17                 System.out.println("第一步:等待送货");
    18                 Thread.sleep(5000);  // 模拟送货时间
    19                 System.out.println("第一步:快递送到");
    20                 return new Chuju();
    21             }
    22             
    23         };
    24         FutureTask<Chuju> task = new FutureTask<Chuju>(onlineShopping);
    25         new Thread(task).start();
    26         // 第二步 去超市购买食材
    27         Thread.sleep(2000);  // 模拟购买食材时间
    28         Shicai shicai = new Shicai();
    29         System.out.println("第二步:食材到位");
    30         // 第三步 用厨具烹饪食材
    31         if (!task.isDone()) {  // 联系快递员,询问是否到货
    32             System.out.println("第三步:厨具还没到,心情好就等着(心情不好就调用cancel方法取消订单)");
    33         }
    34         Chuju chuju = task.get();
    35         System.out.println("第三步:厨具到位,开始展现厨艺");
    36         cook(chuju, shicai);
    37         
    38         System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    39     }
    40     
    41     //  用厨具烹饪食材
    42     static void cook(Chuju chuju, Shicai shicai) {}
    43     
    44     // 厨具类
    45     static class Chuju {}
    46     
    47     // 食材类
    48     static class Shicai {}
    49 
    50 }

    结果

    1 第一步:下单
    2 第一步:等待送货
    3 第二步:食材到位
    4 第三步:厨具还没到,心情好就等着(心情不好就调用cancel方法取消订单)
    5 第一步:快递送到
    6 第三步:厨具到位,开始展现厨艺
    7 总共用时5005ms

    下面具体分析一下这段代码:

    1)把耗时的网购厨具逻辑,封装到了一个Callable的call方法里面。

    复制代码
    public interface Callable<V> {
        /**
         * Computes a result, or throws an exception if unable to do so.
         *
         * @return computed result
         * @throws Exception if unable to compute a result
         */
        V call() throws Exception;
    }
    复制代码

     Callable接口可以看作是Runnable接口的补充,call方法带有返回值,并且可以抛出异常。

    2)把Callable实例当作参数,生成一个FutureTask的对象,然后把这个对象当作一个Runnable,作为参数另起线程。

    public class FutureTask<V> implements RunnableFuture<V>
    public interface RunnableFuture<V> extends Runnable, Future<V>
    复制代码
    public interface Future<V> {
    
        boolean cancel(boolean mayInterruptIfRunning);
    
        boolean isCancelled();
    
        boolean isDone();
    
        V get() throws InterruptedException, ExecutionException;
    
        V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    }
    复制代码

    这个继承体系中的核心接口是Future。Future的核心思想是:一个方法f,计算过程可能非常耗时,等待f返回,显然不明智。可以在调用f的时候,立马返回一个Future,可以通过Future这个数据结构去控制方法f的计算过程。

    这里的控制包括:

    get方法:获取计算结果(如果还没计算完,也是必须等待的)

    cancel方法:还没计算完,可以取消计算过程

    isDone方法:判断是否计算完

    isCancelled方法:判断计算是否被取消

    这些接口的设计很完美,FutureTask的实现注定不会简单,后面再说。

    3)在第三步里面,调用了isDone方法查看状态,然后直接调用task.get方法获取厨具,不过这时还没送到,所以还是会等待3秒。对比第一段代码的执行结果,这里我们节省了2秒。这是因为在快递员送货期间,我们去超市购买食材,这两件事在同一时间段内异步执行!!!

    JDK8 中 CompletableFuture 是非常强大的 Future 的扩展功能。

    模拟代码:

    public class CompleteFutureTests {
        public static void main(String[] args) throws Exception {
    //1
    CompletableFuture<String> bookFuture = CompletableFuture.supplyAsync(() -> {
    System.out.println("000000000");
    return "xanyi000001";
    });

    //2
    CompletableFuture<String> tableFuture = CompletableFuture.supplyAsync(() -> {
    System.out.println("沉睡5秒");
    try {
    Thread.sleep(5000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    return "xanyi111110";
    });

    //CompletableFuture.allOf(bookFuture, tableFuture).join();
    CompletableFuture.anyOf(bookFuture, tableFuture).join();

    System.out.println("book -> " + bookFuture.get());
    System.out.println("tale -> " + tableFuture.get());
    }
    }

    allOf 工厂方法接收一个由CompletableFuture 构成的数组,数组中的所有 Completable-Future 对象执行完成之后,它返回一个 CompletableFuture<Void> 对象。这意味着,如果你需要等待多个 CompletableFuture 对象执行完毕,对 allOf 方法返回的
    CompletableFuture 执行 join 操作可以等待CompletableFuture执行完成。

    或者你可能希望只要 CompletableFuture 对象数组中有任何一个执行完毕就不再等待,在这种情况下,你可以使用一个类似的工厂方法 anyOf

  • 相关阅读:
    Windows常用命令
    Linux常用命令
    SpringBoot实现动态数据源切换及单库事务控制
    ArrayList与LinkedList
    java学习笔记之集合—ArrayList源码解析
    java学习笔记之反射—反射和工厂模式
    java学习笔记之反射—Class类实例化和对象的反射实例化
    java学习笔记之IO编程—对象序列化
    java学习笔记之IO编程—打印流和BufferedReader
    java学习笔记之IO编程—内存流、管道流、随机流
  • 原文地址:https://www.cnblogs.com/xiexy/p/9753088.html
Copyright © 2020-2023  润新知