线程回顾-初始化线程的四种方式
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>());
}
核心和最大线程数量都是我们穿进来的参数,都不可回收
特点:
固定大小:该线程池的核心线程数和最大线程数都是
nThreads
,也就是线程池的大小是固定的,不会自动增加或减少线程数量。无界队列:该线程池使用
LinkedBlockingQueue
作为任务队列,它是一个没有容量限制的阻塞队列,可以无限制地存放任务。核心线程一直存活:即使没有任务需要执行,核心线程也会一直存活,不会被回收。
超出核心线程数的任务会被放入队列:当任务数量超过核心线程数时,多余的任务会被放入队列中等待执行。
优点:
稳定性:固定大小线程池适用于长期运行的任务场景,可以保持一定数量的核心线程一直存活,不会因为任务的数量而频繁创建和销毁线程,保持线程池的稳定性。
资源控制:由于线程数量是固定的,可以更好地控制系统资源的使用情况,避免线程数量无限增长导致资源浪费和系统崩溃。
有界队列:使用有界队列可以避免因为任务量过大导致内存溢出,队列有限制可以控制任务提交速度,使得系统能够更好地应对高并发场景。
缺点:
不适应任务突发:由于线程池的大小是固定的,如果任务数量突然增加,可能会导致队列中的任务堆积,从而影响系统响应性能。
有界队列可能导致任务丢失:如果有界队列满了,新提交的任务可能会被拒绝或丢弃,这可能导致一些任务无法被及时处理。
总体来说,固定大小线程池适用于长期运行的任务,特别是在任务数量相对稳定且可控的情况下,它可以提供稳定的性能表现,并且能够更好地控制系统资源的使用。然而,在任务量不稳定或任务执行时间较长的情况下,可能会导致队列堆积或任务丢失的问题,建议根据具体业务场景来选择合适的线程池类型和配置参数。
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 都是可以获取返回结果的
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 这个任务继续提交给线程池来进行执行。
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();