异步与线程池-异步编排详解

线程回顾-初始化线程的四种方式

1.继承Thread

public class TestThread {

    public static void main(String[] args) {
        System.out.println("main...start");
        new Thread01().start();
        System.out.println("main...end");
    }
    
    public static class Thread01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程的id"+Thread.currentThread().getId());
        }
    }
}
输出:
main...start
main...end
当前线程的id12

2.实现Runable接口

public class TestThread {
    public static void main(String[] args) {
        System.out.println("main...start");
        new Thread(new Thread02()).start();
        System.out.println("main...end");
    }
    
    public static class Thread02 implements Runnable{
        @Override
        public void run() {
            System.out.println("当前线程的id"+Thread.currentThread().getId());
        }
    }
}
输出:
main...start
main...end
当前线程的id12
原因:Thread类实现了Runnable接口,而 Runnable 接口只是定义了一个 run() 方法,
需要通过 Thread 类的构造方法来将 Runnable 对象转换为线程对象。继承了 Thread 
类的子类可以直接调用 start() 方法启动线程,因为子类已经继承了 Thread 类的所有
方法和属性,包括 start() 方法。而实现了 Runnable 接口的类需要将自己传递给 Thread 
类的构造方法,将 Runnable 对象转换为 Thread 对象,然后才能调用 start() 方法启动线程。

3.实现Callable接口 + FutureTask(可以拿到返回结果,可以处理异常)

package com.syctest.test1.test;

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

public class TestThread {
    public static void main(String[] args) {
        System.out.println("main...start");
        FutureTask<Integer> integerFutureTask = new FutureTask<>(new Thread03());
        new Thread(integerFutureTask).start();
         System.out.println("线程的返回结果:"+integerFutureTask.get());//会阻塞主线程
        System.out.println("main...end");
    }

    public static class Thread03 implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程的id"+Thread.currentThread().getId());
            return 10/2;
        }
    }
}
//输出
main...start
当前线程的id12
线程的返回结果:5
main...end

在业务代码中,以上三种基本都不用了,可能存在会把资源耗尽的情况!!

4.线程池(get线程池提交任务即可)

public class TestThread {
    //固定数量的线程池,一般来说一个系统中只会存在1-2个线程池
   public static ExecutorService executorService = Executors
.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start");
        executorService.execute(new Thread01());
        System.out.println("main...end");
    }

    public static class Thread01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程的id"+Thread.currentThread().getId());
        }
    }
}

以上四种的区别:1.2不能得到返回值,3可用获取到返回值,但是1,2,3都做不到控制资源,比如同时来咯1000000个请求,他们就会开启那么多个线程,可能导致资源耗尽,而线程池则可以保证系统的稳定性

线程池

1.常见的四种线程池

1.1可缓存线程池

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

//使用Executors.newCachedThreadPool获取

特点:

自动扩容:该线程池会根据任务的数量自动增加线程数量,当有新任务提交时,如果有空闲线程可用,会复用空闲线程;如果没有空闲线程,则会创建新的线程来处理任务。

无限制的线程数量:线程数量上限是Integer.MAX_VALUE,也就是理论上可以无限制地创建新线程来处理任务。

空闲线程回收:当线程在60秒内没有被使用时,如果线程池的大小超过了核心线程数(0),则会将空闲的线程回收,以减少资源消耗。

SynchronousQueue阻塞队列:该线程池使用SynchronousQueue作为任务队列,它是一个没有容量的阻塞队列,每个插入操作必须等待一个相应的删除操作,反之亦然。因此,如果没有可用的线程来执行任务,新任务将会在提交时阻塞,直到有线程可用为止。

优点:

高效性:新缓存线程池适用于短期异步任务,可以快速地创建线程来处理任务,从而提高任务处理的并发性和响应速度。

资源利用:当有大量短期异步任务需要处理时,它可以充分利用系统资源,允许并行地执行多个任务。

适应性:线程池大小可以根据任务的数量动态扩展,适应任务负载的变化。

缺点:

线程数量无限制:由于线程数量上限是Integer.MAX_VALUE,如果任务量过大或任务执行时间过长,可能会导致线程数量无限增长,最终耗尽系统资源,导致系统崩溃。

可能引发过多线程竞争:在任务数量较大的情况下,由于会同时创建大量线程,可能会引发过多的线程竞争,导致性能下降。

可能导致资源浪费:如果任务提交速度远远大于任务处理速度,可能会导致大量的线程空闲,造成资源浪费。

由于该线程池的线程数量无限制,一般适用于短期异步任务的场景,建议在使用时结合实际业务需求和系统资源状况来进行选择和配置。在高并发、长期运行的任务场景中,可能更适合使用固定大小的线程池或其他类型的线程池。

1.2 newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
核心和最大线程数量都是我们穿进来的参数,都不可回收

特点:

  1. 固定大小:该线程池的核心线程数和最大线程数都是nThreads,也就是线程池的大小是固定的,不会自动增加或减少线程数量。

  2. 无界队列:该线程池使用LinkedBlockingQueue作为任务队列,它是一个没有容量限制的阻塞队列,可以无限制地存放任务。

  3. 核心线程一直存活:即使没有任务需要执行,核心线程也会一直存活,不会被回收。

  4. 超出核心线程数的任务会被放入队列:当任务数量超过核心线程数时,多余的任务会被放入队列中等待执行。

优点:

  1. 稳定性:固定大小线程池适用于长期运行的任务场景,可以保持一定数量的核心线程一直存活,不会因为任务的数量而频繁创建和销毁线程,保持线程池的稳定性。

  2. 资源控制:由于线程数量是固定的,可以更好地控制系统资源的使用情况,避免线程数量无限增长导致资源浪费和系统崩溃。

  3. 有界队列:使用有界队列可以避免因为任务量过大导致内存溢出,队列有限制可以控制任务提交速度,使得系统能够更好地应对高并发场景。

缺点:

  1. 不适应任务突发:由于线程池的大小是固定的,如果任务数量突然增加,可能会导致队列中的任务堆积,从而影响系统响应性能。

  2. 有界队列可能导致任务丢失:如果有界队列满了,新提交的任务可能会被拒绝或丢弃,这可能导致一些任务无法被及时处理。

总体来说,固定大小线程池适用于长期运行的任务,特别是在任务数量相对稳定且可控的情况下,它可以提供稳定的性能表现,并且能够更好地控制系统资源的使用。然而,在任务量不稳定或任务执行时间较长的情况下,可能会导致队列堆积或任务丢失的问题,建议根据具体业务场景来选择合适的线程池类型和配置参数。 

1.3newScheduledThreadPoo:创建一个定长线程池,支持定时及周期性任务执行。  

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

不常用

1.4 Executors.newSingleThreadExecutor():单线程线程池

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

2.原生方法

原生方法ThreadPoolExecutor(为了解决Executors提供的四种快速创建线程池出现的OOM(内存不足)问题,推荐使用ThreadPoolExecutor的方式,按业务、按需创建线程池。设置合理的corePoolSize、maximumPoolSize、keepAliveTime、workQueue、handler.

首先来看七个参数的含义:

 

corePoolSize:核心线程数量,线程池创建好以后就准备就绪的线程,他会一直存在,直至容器销毁,除非我们设置了(aLLowCore ThreadTimeout)核心线程超时时间

maximumPoolSize:最大线程数,控制资源

KeepAliveTime:存活时间,如果当前线程数量大于核心线程数量,这个值就是他的最大空闲等待时间

unit:时间单位

BLockingQueue<Runnable worhQueue;阻塞队列。如果有很多,就会将多的任务放在任务队列里,只要有空闲线程,就会从队列出取出任务进行执行

threadFactory:线程的创建工厂

handler:拒绝策略,如果队列满了,按照我们指定的策略执行

3.常见面试问题: 

Q1:core:7 max 20 queue50 并发一百怎么执行:

7个会立即执行,50个会进入队列, 再开13个线程执行,剩下的30个就会使用我们的拒绝策略,如果不想抛弃,

 Q2:使用线程池的好处:

1.降低资源的消耗,通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗

2.提高响应速度: 因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行

3.提高线程的可管理性 线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池讲行统一分配

 

completableFuture异步编排

业务场景:一个在线数据导出功能,从数据库A导入到数据库B,前端传来项目ID,需要将与这个项目关联的一系列信息,如:项目信息 供应商信息 设备信息 设备分拨信息 配件表等一系列信息导出,

假如不使用异步编排,从上往下单线程执行:大约需要话10S,好,先介绍异步编排的知识,再最后给出我所做的代码

1.创建异步对象

    public static CompletableFuture<Void> runAsync(Runnable runnable) {
        
    }


    public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
      
    }

    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
       
    }

    
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor) {
      
    }

使用CompletableFuture调用对应的API即可,上面四个API的区别是:

1、runXxxx 都是没有返回结果的,supplyXxx 都是可以获取返回结果的

2 、可以传入自定义的线程池(executor参数),否则就用默认的线程池;
示例
        CompletableFuture<ProjectInfo> infoFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getId()+"线程在发送项目数据");
            
            //1.发送项目数据
            ProjectInfo projectInfo = projectInfoService.getById(projectId);
            if (null == projectInfo) {
                throw new XBException(3000, "参数有误");
            }

           
            try {
                //sendUri():发送请求保存数据的自定义函数
                String onLineAddProject = sendUri(projectInfo, "onLineAddProject");
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }

            return projectInfo;

        }, MyThreadPool.service);

//这段代码传递了两个参数,第一个参数使用lambda表达式传入了我们的业务代码,第二个参数为我的自定义线程,在最后会贴出我的完整业务代码,

2,计算完成时回调方法

    public CompletableFuture<T> whenComplete(
       
    }

    public CompletableFuture<T> whenCompleteAsync(
      
    }

    public CompletableFuture<T> whenCompleteAsync(
       
    }
    public CompletableFuture<T> exceptionally(
        
    }
whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。

whenComplete 和 whenCompleteAsync 的区别:
whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程
执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)
示例
 CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            return 10 / 0;
        }, service).whenComplete((res,exception)->{
            //虽然能得到异常信息,但是无法修改返回值
            System.out.println("当前线程" + Thread.currentThread().getId());
            System.out.println("t:u:"+res+exception);
        }).exceptionally((exception1)->{
            //可以对返回值进行修改
            System.out.println("exception1"+exception1);//打印:exception1java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
            return 100;
        });
        System.out.println("返回值"+integerCompletableFuture.get());//打印我们修改好的返回值:100
        System.out.println("main线程结束");

3.handle()方法

public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);

handle() 方法的作用是在异步操作完成后对结果进行处理,并返回一个新的 CompletableFuture 对象,该对象包含了处理后的结果或异常。无论原始异步操作是正常完成还是发生异常,handle() 方法都会执行。

示例:

    //2.handle()方法:
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread().getId());
            return 10 / 2;
        }, service).handle((res,exception)->{
            System.out.println("res:"+res+"exception:"+exception);
            if (res!=null){
                return res;
            }

            return 0;//出现异常,返回0
        });
        System.out.println("返回值"+integerCompletableFuture.get());//打印我们修改好的返回值:101
        System.out.println("main线程结束");

4.线程串行化方法(注意是串行!!)

    //thenApply:接收我们上一步的返回结果,并返回出去一个结果
    public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)

    public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)

    public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) 

    
    //thenAccept:(Accept:接受)需要上一步的执行结果
    public CompletableFuture<Void> thenAccept(Consumer<? super T> action)

    public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)

    public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)

    //thenRun:不需要上一步的执行结果
    public CompletableFuture<Void> thenRun(Runnable action)

    public CompletableFuture<Void> thenRunAsync(Runnable action)

    public CompletableFuture<Void> thenRunAsync(Runnable action,
                                                Executor executor)

示例:

  //串行化
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步任务1开始");
            System.out.println("异步任务1结束");
            return 1;
        }, service).thenRunAsync(() -> {
            System.out.println("异步任务2开始");
            System.out.println("异步任务2结束");
        }, service);

//        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("异步任务1开始");
//            try {
//                Thread.sleep(3000);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            System.out.println("异步任务1结束");
//            return 1 ;
//        }, service).thenRunAsync(()->{
//            System.out.println("异步任务2开始");
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            System.out.println("异步任务2结束");
//        },service);

以上都是开启线程的方法,那么怎么对他们进行编排呢?

5.两任务组合,都要完成!

    public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
                                                Runnable action)

    public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
                                                     Runnable action)

    public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
                                                     Runnable action,
                                                     Executor executor)

    public <U,V> CompletableFuture<V> thenCombine(
        CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn)

    public <U,V> CompletableFuture<V> thenCombineAsync(
        CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn)

    public <U,V> CompletableFuture<V> thenCombineAsync(
        CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn, Executor executor)


    public <U> CompletableFuture<Void> thenAcceptBoth(
        CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action) 

    public <U> CompletableFuture<Void> thenAcceptBothAsync(
        CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action)

    public <U> CompletableFuture<Void> thenAcceptBothAsync(
        CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action, Executor executor)



runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后,处理该任务

thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值

thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值。

示例:

 CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步任务1开始");
            System.out.println("异步任务1结束");
            return 1;
        }, service);

        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步任务2开始");
            System.out.println("异步任务2结束");
            return "hellow";
        }, service);

        future01.runAfterBothAsync(future02,()->{
            System.out.println("任务三开始");
        },service);
//输出
main线程开始
异步任务1开始
异步任务1结束
异步任务2开始
异步任务2结束
任务三开始
    
       CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步任务1开始");
            System.out.println("异步任务1结束");
            return 1;
        }, service);

        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步任务2开始");
            System.out.println("异步任务2结束");
            return "hellow";
        }, service);

        CompletableFuture<Integer> integerCompletableFuture = future01.thenCombineAsync(future02, (f1, f2) -> {
            System.out.println("任务三开始:前两个任务的返回值: " + "f1: " + f1 + "f2: " + f2);
            return 100;
        }, service);

        System.out.println("最终返回结果"+integerCompletableFuture.get());
打印:main线程开始
异步任务1开始
异步任务1结束
异步任务2开始
异步任务2结束
任务三开始:前两个任务的返回值: f1: 1f2: hellow
最终返回结果100

 

6.两个任务一个完成

    public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
                                                  Runnable action)

    public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                       Runnable action)

    public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                       Runnable action,
                                                       Executor executor)

    public <U> CompletableFuture<U> applyToEither(
        CompletionStage<? extends T> other, Function<? super T, U> fn)

    public <U> CompletableFuture<U> applyToEitherAsync(
        CompletionStage<? extends T> other, Function<? super T, U> fn) 

    public <U> CompletableFuture<U> applyToEitherAsync(
        CompletionStage<? extends T> other, Function<? super T, U> fn,
        Executor executor) 

    public CompletableFuture<Void> acceptEither(
        CompletionStage<? extends T> other, Consumer<? super T> action)

    public CompletableFuture<Void> acceptEitherAsync(
        CompletionStage<? extends T> other, Consumer<? super T> action)

    public CompletableFuture<Void> acceptEitherAsync(
        CompletionStage<? extends T> other, Consumer<? super T> action,
        Executor executor)

applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。
acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返
回值

7.多任务组合

    public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) 


    public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) 

此处示例代码就拿我真实项目的代码进行展示:

1.首先是我的自定义线程池:

package com.xinbiao.threadPool;


import java.util.concurrent.*;
public class MyThreadPool {
    public static final ExecutorService service=new ThreadPoolExecutor(20, 200,
            10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10000),Executors.defaultThreadFactory()
            ,new ThreadPoolExecutor.AbortPolicy());

}

2.是业务代码中用到的发送请求的方法:

    public String sendUri(Object object,String uri) throws JsonProcessingException {
        RestTemplate restTemplate = new RestTemplate();
        String apiUrl = "http://***:8080/inside/projectInfo/"; // 替换为目标API的URL
        ObjectMapper objectMapper = new ObjectMapper();
        // 构造请求头,这里设置Content-Type为application/json,根据API的要求进行设置
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        //封装请求体和请求头
        String str = objectMapper.writeValueAsString(object);
        HttpEntity<String> supplierInfoRequestEntity = new HttpEntity<>(str, headers);
        System.out.println(apiUrl+uri);
        String responseStr = restTemplate.postForObject(apiUrl+uri, supplierInfoRequestEntity, String.class);
        return responseStr;
    }

3.未使用异步的业务代码

 //在线导出数据接口
    @GetMapping("/onLineDataOut")
    public Result onLineDataOut(Long projectId) throws JsonProcessingException {
       

//        1.发送项目数据
        ProjectInfo projectInfo = projectInfoService.getById(projectId);
        if (null==projectInfo){
            throw new XBException(3000,"参数有误");
        }
        // 构造请求体数据,这里使用JSON数据作为示例,根据API的要求构造请求体
        if(projectInfo.getProjectTractImg()!=null){//检查合同是否为空
            projectInfo.setStringImg(ImgIOJsonOutputUtils.encode(projectInfo.getProjectTractImg()));
            projectInfo.setProjectTractImg(null);
        }
        String onLineAddProject = sendUri(projectInfo, "onLineAddProject");

        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {

        }, MyThreadPool.service);

//        2.发送供应商信息
        SupplierInfo supplierInfo = supplierInfoService.getOne(new QueryWrapper<SupplierInfo>().eq("SUPPLIER_ID",projectInfo.getSupplierId()));
        if(supplierInfo!=null){
            if(supplierInfo.getSupplierAuthorized()!=null){
                supplierInfo.setStringAuthorized(ImgIOJsonOutputUtils.encode(supplierInfo.getSupplierAuthorized()));

            }
            if(supplierInfo.getSupplierBusinessCertificate()!=null){
                supplierInfo.setStringBusinessCertificate(ImgIOJsonOutputUtils.encode(supplierInfo.getSupplierBusinessCertificate()));

            }
            if(supplierInfo.getSupplierBusinessLicense()!=null){
                supplierInfo.setStringBusinessLicense(ImgIOJsonOutputUtils.encode(supplierInfo.getSupplierBusinessLicense()));

            }

            supplierInfo.setSupplierAuthorized(null);
            supplierInfo.setSupplierBusinessCertificate(null);
            supplierInfo.setSupplierBusinessLicense(null);

            String onLineAddSupplier = sendUri(supplierInfo, "onLineAddSupplier");

        }

        //3.查询设备信息  这个可能会更新(供应商更新证件信息),所以每次都导
        FacilityInfo facilityInfo = facilityInfoService.getOne(new QueryWrapper<FacilityInfo>().eq("PROJECT_ID", projectId));

        // 将图片使用base64进行编码处理,二进制数据不能用于json传输,所以,赋值为空
        if(facilityInfo!=null){
            if(facilityInfo.getFacilityProductionImg()!=null){
                facilityInfo.setStringFacilityProductionImg(ImgIOJsonOutputUtils.encode(facilityInfo.getFacilityProductionImg()));
            }
            if(facilityInfo.getFacilityRegisterImg()!=null){
                facilityInfo.setStringFacilityRegisterImg(ImgIOJsonOutputUtils.encode(facilityInfo.getFacilityRegisterImg()));

            }
            if(facilityInfo.getInstructionBookImg()!=null){
                facilityInfo.setStringInstructionBookImg(ImgIOJsonOutputUtils.encode(facilityInfo.getInstructionBookImg()));

            }
            if(facilityInfo.getWarrantyCardImg()!=null){
                facilityInfo.setStringWarrantyCardImg(ImgIOJsonOutputUtils.encode(facilityInfo.getWarrantyCardImg()));

            }

            facilityInfo.setFacilityProductionImg(null);
            facilityInfo.setFacilityRegisterImg(null);
            facilityInfo.setWarrantyCardImg(null);
            facilityInfo.setInstructionBookImg(null);

            String facilityInfoResponse = sendUri(facilityInfo, "onLineAddFacilityInfo");
        }

        //4.导出设备详细表信息
        List<FacilityDetailInfo> facilityDetailInfoList = facilityDetailInfoService.list(new QueryWrapper<FacilityDetailInfo>()
                .eq("FACILITY_ID", facilityInfo.getFacilityId()));
        if(facilityDetailInfoList!=null&&facilityDetailInfoList.size()!=0){
            String onLineAddFacilityDetailInfo = sendUri(facilityDetailInfoList, "onLineAddFacilityDetailInfo");
        }

//        5.设备病区中间表
        List<InpatientFacilityInfo> inpatientList = inpatientFacilityInfoService.list(new QueryWrapper<InpatientFacilityInfo>()
                .eq("FACILITY_ID", facilityInfo.getFacilityId()));
        if(inpatientList!=null){
            String onLineAddInpatientList = sendUri(inpatientList, "onLineAddInpatientList");
        }

        //6.导出配件信息  包含兼容配件和原装
        List<PartsInfo> partsInfoList = partsInfoService.list(new QueryWrapper<PartsInfo>().eq("FACILITY_ID", facilityInfo.getFacilityId())
        );
        if(partsInfoList!=null){
            sendUri(partsInfoList,"onLineAddPart");
        }

        // 7.真正的配件信息表  和配件信息表一样,没有导过的才进行导入
        List<FacilityPartsInfo> facilityPartsInfos = facilityPartsInfoService.list(new QueryWrapper<FacilityPartsInfo>()
                .eq("FACILITY_ID", facilityInfo.getFacilityId()));
        if(facilityPartsInfos!=null){
            System.out.println("真正的配件:"+facilityPartsInfos.size());
            sendUri(facilityPartsInfos,"onLineAddFacilityPart");
        }

        //8.导出维修信息
        List<FacilityMaintainInfo> over_time = facilityMaintainInfoService.list(new QueryWrapper<FacilityMaintainInfo>().isNotNull("OVER_TIME"));
        if(over_time!=null&&over_time.size()!=0){
            sendUri(facilityPartsInfos,"onLineAddFacilityMain");
        }

        //9.科室 导出科室信息
        System.out.println("报错出"+facilityInfo.getHospitalId());


        List<DepartmentInfo> departmentInfos = departmentInfoService.list(new QueryWrapper<DepartmentInfo>()
                .eq("HOSPITAL_ID", facilityInfo.getHospitalId())
        );

        if(departmentInfos!=null){
           sendUri(departmentInfos,"onLineAddDepartment");

            List<Long> departmentIds = departmentInfos.stream().map(DepartmentInfo::getDepartmentId).collect(Collectors.toList());
            //病区
            List<InpatientInfo> inpatientInfoList = inpatientInfoService.list(new QueryWrapper<InpatientInfo>()
                    .in("DEPARTMENT_ID", departmentIds));
            if(inpatientInfoList!=null){
                sendUri(inpatientInfoList,"onLineAddInpatientInfo");
            }
        }

        //10.软件接口信息  可能会发生修改,所有每次都导入在那边进行覆盖(更新)
        List<SoftwareJoinInfo> list = softwareJoinInfoService.list(new QueryWrapper<SoftwareJoinInfo>().eq("FACILITY_ID", facilityInfo.getFacilityId()));
        if(list!=null){//判空相当于判断了这个是不是软件接口的设备
            sendUri(list,"onLineAddSoftware");
        }

        //11.医院账号  科室账号 导出
        List<HospitalAccountInfo> hospitalAccountInfos = hospitalAccountInfoService.list(new QueryWrapper<HospitalAccountInfo>()
                .eq("HOSPITAL_ID", projectInfo.getHospitalId()));
        List<DepartmentAccountInfo> departmentAccountInfos = departmentAccountInfoService.list(new QueryWrapper<DepartmentAccountInfo>()
                .eq("HOSPITAL_ID", projectInfo.getHospitalId()));

        if(hospitalAccountInfos!=null){
            sendUri(hospitalAccountInfos,"onLineAddHospitalAccount");
        }
        if(departmentAccountInfos!=null){
            sendUri(departmentAccountInfos,"onLineAddDepartmentAccount");
        }

        return Result.success(null);
    }

本地测试花费时间大约20s左右

优化后:

  

    //在线导出数据接口
    @GetMapping("/onLineDataOut")
    public Result onLineDataOut(Long projectId) throws JsonProcessingException, ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        //因为返回的项目信息别的地方要用,所以使用supply方法
        CompletableFuture<ProjectInfo> infoFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getId()+"线程在发送项目数据");
//        1.发送项目数据
            ProjectInfo projectInfo = projectInfoService.getById(projectId);
            if (null == projectInfo) {
                throw new XBException(3000, "参数有误");
            }
            // 构造请求体数据,这里使用JSON数据作为示例,根据API的要求构造请求体
            if (projectInfo.getProjectTractImg() != null) {//检查合同是否为空
                projectInfo.setStringImg(ImgIOJsonOutputUtils.encode(projectInfo.getProjectTractImg()));
                projectInfo.setProjectTractImg(null);
            }
            try {
                String onLineAddProject = sendUri(projectInfo, "onLineAddProject");
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            return projectInfo;
        }, MyThreadPool.service);
        //异步请求facility信息
        CompletableFuture<FacilityInfo> facilityFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getId()+"线程在发送facility信息");
            //3.查询设备信息  这个可能会更新(供应商更新证件信息),所以每次都导
            FacilityInfo facilityInfo = facilityInfoService.getOne(new QueryWrapper<FacilityInfo>().eq("PROJECT_ID", projectId));
            // 将图片使用base64进行编码处理,二进制数据不能用于json传输,所以,赋值为空
            if (facilityInfo != null) {
                if (facilityInfo.getFacilityProductionImg() != null) {
                    facilityInfo.setStringFacilityProductionImg(ImgIOJsonOutputUtils.encode(facilityInfo.getFacilityProductionImg()));
                }
                if (facilityInfo.getFacilityRegisterImg() != null) {
                    facilityInfo.setStringFacilityRegisterImg(ImgIOJsonOutputUtils.encode(facilityInfo.getFacilityRegisterImg()));

                }
                if (facilityInfo.getInstructionBookImg() != null) {
                    facilityInfo.setStringInstructionBookImg(ImgIOJsonOutputUtils.encode(facilityInfo.getInstructionBookImg()));

                }
                if (facilityInfo.getWarrantyCardImg() != null) {
                    facilityInfo.setStringWarrantyCardImg(ImgIOJsonOutputUtils.encode(facilityInfo.getWarrantyCardImg()));

                }

                facilityInfo.setFacilityProductionImg(null);
                facilityInfo.setFacilityRegisterImg(null);
                facilityInfo.setWarrantyCardImg(null);
                facilityInfo.setInstructionBookImg(null);

                try {
                    String facilityInfoResponse = sendUri(facilityInfo, "onLineAddFacilityInfo");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
            return facilityInfo;
        }, MyThreadPool.service);
        CompletableFuture<Void> mainFuture = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getId()+"线程在发送维修数据");
            //8.导出维修信息
            List<FacilityMaintainInfo> over_time = facilityMaintainInfoService.list(new QueryWrapper<FacilityMaintainInfo>().isNotNull("OVER_TIME"));
            if (over_time != null && over_time.size() != 0) {
                try {
                    sendUri(over_time, "onLineAddFacilityMain");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }, MyThreadPool.service);


        //异步编排执行发送供应商信息
        CompletableFuture<Void> supplierFuture = infoFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送供应商信息");
            // 2.发送供应商信息
            SupplierInfo supplierInfo = supplierInfoService.getOne(new QueryWrapper<SupplierInfo>().eq("SUPPLIER_ID", res.getSupplierId()));
            if (supplierInfo != null) {
                if (supplierInfo.getSupplierAuthorized() != null) {
                    supplierInfo.setStringAuthorized(ImgIOJsonOutputUtils.encode(supplierInfo.getSupplierAuthorized()));
                }
                if (supplierInfo.getSupplierBusinessCertificate() != null) {
                    supplierInfo.setStringBusinessCertificate(ImgIOJsonOutputUtils.encode(supplierInfo.getSupplierBusinessCertificate()));
                }
                if (supplierInfo.getSupplierBusinessLicense() != null) {
                    supplierInfo.setStringBusinessLicense(ImgIOJsonOutputUtils.encode(supplierInfo.getSupplierBusinessLicense()));
                }

                supplierInfo.setSupplierAuthorized(null);
                supplierInfo.setSupplierBusinessCertificate(null);
                supplierInfo.setSupplierBusinessLicense(null);

                try {
                    String onLineAddSupplier = sendUri(supplierInfo, "onLineAddSupplier");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }, MyThreadPool.service);
        //异步编排发送医院账号  科室账号 导出   //11.医院账号  科室账号 导出
        CompletableFuture<Void> hospitalAccountFuture1 = infoFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送医院账号信息");
            List<HospitalAccountInfo> hospitalAccountInfos = hospitalAccountInfoService.list(new QueryWrapper<HospitalAccountInfo>()
                    .eq("HOSPITAL_ID", res.getHospitalId()));
            if (hospitalAccountInfos != null) {
                try {
                    sendUri(hospitalAccountInfos, "onLineAddHospitalAccount");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }, MyThreadPool.service);
        CompletableFuture<Void> departmentAccountFuture2 = infoFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送科室账号信息");
            List<DepartmentAccountInfo> departmentAccountInfos = departmentAccountInfoService.list(new QueryWrapper<DepartmentAccountInfo>()
                    .eq("HOSPITAL_ID", res.getHospitalId()));
            if (departmentAccountInfos != null) {
                try {
                    sendUri(departmentAccountInfos, "onLineAddDepartmentAccount");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }

        }, MyThreadPool.service);
        // 合并供应商、医院账号和科室账号的请求
        CompletableFuture<Void> supplierHospitalDepartmentFuture = CompletableFuture.allOf(
                supplierFuture, hospitalAccountFuture1,departmentAccountFuture2);


        //编排facilityFuture完成后的异步任务
        CompletableFuture<Void> voidCompletableFuture = facilityFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送facilityDetail数据");
            //4.导出设备详细表信息
            List<FacilityDetailInfo> facilityDetailInfoList = facilityDetailInfoService.list(new QueryWrapper<FacilityDetailInfo>()
                    .eq("FACILITY_ID", res.getFacilityId()));
            if (facilityDetailInfoList != null && facilityDetailInfoList.size() != 0) {
                try {
                    String onLineAddFacilityDetailInfo = sendUri(facilityDetailInfoList, "onLineAddFacilityDetailInfo");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }, MyThreadPool.service);

        CompletableFuture<Void> voidCompletableFuture1 = facilityFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送设备病区中间表数据");
            //        5.设备病区中间表
            List<InpatientFacilityInfo> inpatientList = inpatientFacilityInfoService.list(new QueryWrapper<InpatientFacilityInfo>()
                    .eq("FACILITY_ID", res.getFacilityId()));
            if (inpatientList != null) {
                try {
                    String onLineAddInpatientList = sendUri(inpatientList, "onLineAddInpatientList");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }, MyThreadPool.service);


        CompletableFuture<Void> voidCompletableFuture2 = facilityFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送配件信息数据");
            //6.导出配件信息  包含兼容配件和原装
            List<PartsInfo> partsInfoList = partsInfoService.list(new QueryWrapper<PartsInfo>().eq("FACILITY_ID", res.getFacilityId())
            );
            if (partsInfoList != null) {
                try {
                    sendUri(partsInfoList, "onLineAddPart");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }, MyThreadPool.service);


        CompletableFuture<Void> voidCompletableFuture3 = facilityFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送真正的配件信息数据");
            // 7.真正的配件信息表  和配件信息表一样,没有导过的才进行导入
            List<FacilityPartsInfo> facilityPartsInfos = facilityPartsInfoService.list(new QueryWrapper<FacilityPartsInfo>()
                    .eq("FACILITY_ID", res.getFacilityId()));
            if (facilityPartsInfos != null) {
                System.out.println("真正的配件:" + facilityPartsInfos.size());
                try {
                    sendUri(facilityPartsInfos, "onLineAddFacilityPart");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }, MyThreadPool.service);

        CompletableFuture<Void> voidCompletableFuture4 = facilityFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送科室信息数据");
            List<DepartmentInfo> departmentInfos = departmentInfoService.list(new QueryWrapper<DepartmentInfo>()
                    .eq("HOSPITAL_ID", res.getHospitalId())
            );

            if (departmentInfos != null) {
                try {
                    sendUri(departmentInfos, "onLineAddDepartment");
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }

                List<Long> departmentIds = departmentInfos.stream().map(DepartmentInfo::getDepartmentId).collect(Collectors.toList());
                //病区
                List<InpatientInfo> inpatientInfoList = inpatientInfoService.list(new QueryWrapper<InpatientInfo>()
                        .in("DEPARTMENT_ID", departmentIds));
                if (inpatientInfoList != null) {
                    try {
                        sendUri(inpatientInfoList, "onLineAddInpatientInfo");
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }, MyThreadPool.service);

        CompletableFuture<Void> voidCompletableFuture5 = facilityFuture.thenAcceptAsync((res) -> {
            System.out.println(Thread.currentThread().getId() + "线程在发送软件接口信息数据");
            //10.软件接口信息  可能会发生修改,所有每次都导入在那边进行覆盖(更新)
            List<SoftwareJoinInfo> list = softwareJoinInfoService.list(new QueryWrapper<SoftwareJoinInfo>().eq("FACILITY_ID", res.getFacilityId()));
            if (list != null) {//判空相当于判断了这个是不是软件接口的设备
                try {
                    sendUri(list, "onLineAddSoftware");
                } catch (JsonProcessingException e) {
                }
            }
        }, MyThreadPool.service);
        CompletableFuture<Void> otherFuture = CompletableFuture.allOf(
                voidCompletableFuture,voidCompletableFuture1,voidCompletableFuture2,voidCompletableFuture3,voidCompletableFuture4,voidCompletableFuture5
        );

        //所有任务完成返回         需要医院数据的                      facility数据的   维修信息不需要其他信息
        CompletableFuture.allOf(supplierHospitalDepartmentFuture,otherFuture,mainFuture).get();
        long end = System.currentTimeMillis();

        System.out.println("话费了"+(end-start));
        return Result.success(null);
    }

大约花费3s左右,思路是这样,首先区分出哪些需要其他线程的返回值,根据源代码我们不难看出返回的projectInfo对象需要被供应商 医院账号 科室账号进行使用   facilityInfo需要被

voidCompletableFuture  voidCompletableFuture1... voidCompletableFuture5进行使用,而
mainFuture则不需要,所有就将需要projectinfo信息的编排为一组:
// 合并供应商、医院账号和科室账号的请求
CompletableFuture<Void> supplierHospitalDepartmentFuture = CompletableFuture.allOf(
        supplierFuture, hospitalAccountFuture1,departmentAccountFuture2);

 facilityInfo信息的为一组:

CompletableFuture<Void> otherFuture = CompletableFuture.allOf(
        voidCompletableFuture,voidCompletableFuture1,voidCompletableFuture2,voidCompletableFuture3,voidCompletableFuture4,voidCompletableFuture5
);

最后将这两组和 mainFuture进行编排

CompletableFuture.allOf(supplierHospitalDepartmentFuture,otherFuture,mainFuture).get();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值