日常开发中我们在一个接口中需要处理多个任务,通常都是串行的,这样导致接口的响应时间是每个任务的执行时间的总和。为了缩短响应时间,通常会使用异步处理多任务。

需求举例:查询书籍基本信息,书籍详细信息,作者信息并将结果数据返回。
假设查询书籍基本信息花费500毫秒,查询书籍详细信息花费500毫秒,查询作者信息花费500毫秒,共计1500毫秒,使用异步处理时间一般都是远小于1500毫秒的。

下面使用异步调用方式优化接口

1、异步任务类

实现 Callable 接口,用来处理带返回结果的任务。taskId 用来区别返回结果集数据

package com.example.demo.task;import java.util.concurrent.Callable;/*** 异步任务* @param <T>*/
public class AsynTaskCallable<T> implements Callable<T>{private String taskId;private Callable<T> task;public AsynTaskCallable(String taskId, Callable<T> task) {this.taskId = taskId;this.task = task;}@Overridepublic T call() throws Exception {T callResult = task.call();TaskResult result = new TaskResult();result.setTaskId(taskId);result.setData(callResult);return (T) result;}}

2、异步任务调用类

用来调用异步任务辅助类,completionService 用来指定线程池执行异步任务,tasks 为带返回结果的任务,可以实现多场景复用,减少重复编写相似的代码。

package com.example.demo.task;import com.sun.istack.internal.NotNull;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;/*** 异步任务调用*/
public class AsynTaskHelper<T> {/*** 使用指定线程池执行异步任务*/private CompletionService<TaskResult<T>> completionService = null;/*** 任务集合*/private List<Callable> tasks = null;/*** 设置线程池* @param executorService 线程池* @return*/public AsynTaskHelper setExecutorService(ExecutorService executorService){completionService = new ExecutorCompletionService(executorService);return this;}/*** 添加任务,返回结果* @param taskId* @param task* @return*/public AsynTaskHelper addTask(String taskId, Callable<T> task) {AsynTaskCallable callProxy = new AsynTaskCallable(taskId, task);if(null == tasks || tasks.isEmpty()){tasks = new ArrayList<>();}tasks.add(callProxy);return this;}/*** 提交任务* @return*/public AsynTaskHelper submit(){if(null != tasks && !tasks.isEmpty()){for (Callable callResult : tasks) {completionService.submit(callResult);}}return this;}/*** 获取返回结果* @return Map<K, V> K为任务Id* @throws ExecutionException* @throws InterruptedException*/public Map<String, T> getResult() throws ExecutionException, InterruptedException {return getResult(2, TimeUnit.SECONDS);}/*** 获取返回结果* @param timeout* @param unit* @return Map<K, V> K为任务Id* @throws InterruptedException* @throws ExecutionException*/public Map<String, T> getResult(long timeout,@NotNull TimeUnit unit) throws InterruptedException, ExecutionException {Map<String, T> result = new HashMap<>();if(null == tasks){return result;}for (int i = 0; i < tasks.size(); i++) {Future<TaskResult<T>> poll = completionService.poll(timeout, unit);if(null != poll){TaskResult<T> task = poll.get();if(null != poll && null != task){result.put(task.getTaskId(), task.getData());}}}return result;}}

3、任务结果类

用来接收异步任务返回结果数据

package com.example.demo.task;/*** 任务结果数据* @param <T>*/
public class TaskResult<T> {/*** 任务Id*/private String taskId;/*** 返回数据*/private T data;public String getTaskId() {return taskId;}public void setTaskId(String taskId) {this.taskId = taskId;}public T getData() {return data;}public void setData(T data) {this.data = data;}@Overridepublic String toString() {return "TaskResult{" +"taskId='" + taskId + '\'' +", data=" + data +'}';}
}

4、异步调用

指定线程池执行任务

ExecutorService executor = Executors.newFixedThreadPool(500);

正常业务操作

//查询Book信息
Callable<Book> bookCall = () -> bookService.get(bookId);
//查询BookDetail信息
Callable<BookDetail> bookDetailCall = () -> bookDetailService.get(bookId);
//查询Author信息
Callable<Author> auhtorCall = () -> authorService.get(bookId);

创建异步任务

//创建异步任务
AsynTaskHelper taskCallors = new AsynTaskHelper().setExecutorService(executor).addTask("book", bookCall).addTask("bookDetail", bookDetailCall).addTask("author", auhtorCall).submit();

获取结果,因为任务是异步的,可能第一时间拿不到结果,这里使用自旋的方式获取结果,如果3秒后还是没有结果则直接返回。

do{Map map = taskCallors.getResult();book = (Book) map.get("book");bookDetail = (BookDetail) map.get("bookDetail");author = (Author) map.get("author");runTime = System.currentTimeMillis() - beginTime;
} while ((null == book || null == bookDetail || null == author) && runTime < 3000);

完整示例调用代码

package com.example.demo.controller;import com.example.demo.domain.Author;
import com.example.demo.domain.Book;
import com.example.demo.domain.BookDetail;
import com.example.demo.service.AuthorService;
import com.example.demo.service.BookDetailService;
import com.example.demo.service.BookService;
import com.example.demo.task.AsynTaskHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;@RestController
public class BookController {@Autowiredprivate BookService bookService;@Autowiredprivate BookDetailService bookDetailService;@Autowiredprivate AuthorService authorService;private ExecutorService executor = Executors.newFixedThreadPool(500);@GetMapping("books5/{bookId}")public Map find5(@PathVariable String bookId) throws ExecutionException, InterruptedException {Map<String, Object> result = new HashMap<>();Long beginTime = System.currentTimeMillis();System.out.println("开始并行查询,开始时间:" + beginTime);//查询Book信息Callable<Book> bookCall = () -> bookService.get(bookId);//查询BookDetail信息Callable<BookDetail> bookDetailCall = () -> bookDetailService.get(bookId);//查询Author信息Callable<Author> auhtorCall = () -> authorService.get(bookId);//创建异步任务AsynTaskHelper taskCallors = new AsynTaskHelper().setExecutorService(executor).addTask("book", bookCall).addTask("bookDetail", bookDetailCall).addTask("author", auhtorCall).submit();Book book = null;BookDetail bookDetail = null;Author author = null;long runTime;do{Map map = taskCallors.getResult();book = (Book) map.get("book");bookDetail = (BookDetail) map.get("bookDetail");author = (Author) map.get("author");runTime = System.currentTimeMillis() - beginTime;} while ((null == book || null == bookDetail || null == author) && runTime < 3000);System.out.println("结束并行查询,总耗时:" + (System.currentTimeMillis() - beginTime));result.put("book", book);result.put("detail", bookDetail);result.put("author", author);return result;}}

通过 AsynTaskHelper 调用异步任务能缩短接口响应时间,进而提升系统并发能力,后续有类似的使用场景也支持复用,减少重复编码工作。

JAVA多线程编程之异步相关推荐

  1. java多线程基础视频_【No996】2020年最新 Java多线程编程核心基础视频课程

    01.课程介绍.mp4 02.多线程编程基础-进程与线程.mp4 03.多线程编程基础-使用多线程-继承Thread类.mp4 04.多线程编程基础-使用多线程-实现Runnable接口.mp4 05 ...

  2. 《Java多线程编程核心技术》——1.5节sleep()方法

    本节书摘来自华章社区<Java多线程编程核心技术>一书中的第1章,第1.5节sleep()方法,作者高洪岩,更多章节内容可以访问云栖社区"华章社区"公众号查看 1.5 ...

  3. Android中的多线程编程与异步处理

    Android中的多线程编程与异步处理 引言 在移动应用开发中,用户体验是至关重要的.一个流畅.高效的应用能够吸引用户并提升用户满意度.然而,移动应用面临着处理复杂业务逻辑.响应用户输入.处理网络请求 ...

  4. 响应式编程(反应式编程)的来龙去脉(同步编程、多线程编程、异步编程再到响应式编程)

    响应式编程的来龙去脉(同步编程.多线程编程.异步编程再到响应式编程) 文章目录 响应式编程的来龙去脉(同步编程.多线程编程.异步编程再到响应式编程) 简介 1. 示例 2. 同步编程 3. 多线程编程 ...

  5. java多线程编程从入门到卓越(超详细总结)

    导读:java多线程编程不太熟?或是听说过?或是想复习一下?找不到好的文章?别担心我给你们又安利一波,文章内容很全,并且考虑到很多开发中遇到的问题和解决方案.循环渐进,通俗易懂,文章较长,建议收藏再看 ...

  6. Java 多线程编程核心技术

    课程介绍 多线程编程在最大限度利用计算资源.提高软件服务质量方面扮演着至关重要的角色,而掌握多线程编程也成为了广大开发人员所必须要具备的技能. 本课程以基本概念.原理方法为主线,每篇文章结合大量演示实 ...

  7. java多线程编程01---------基本概念

    一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分,尤其是对于小白,次一系列文章的将会对多线程编程及其原理进行介绍,希望对正在多线程中碰壁的小 ...

  8. java多线程编程同步方法_实践【Java多线程编程核心技术】系列:同步方法造成的无限等待...

    本文实践来自于[Java多线程编程核心技术]一书! 同步方法容易造成死循环,如-- 类Service.java: package service; public class Service { syn ...

  9. java超线程_超线程多核心下Java多线程编程技术分析

    在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,本文主要讲述超线程多核心下Java多线程编程技术分析,更多Java专业知识,广州疯狂 ...

  10. Java多线程编程实战指南

    内容简介 随着CPU 多核时代的到来,多线程编程在充分利用计算资源.提高软件服务质量方面扮演了越来越重要的角色.而解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案.然而,多线程编 ...

最新文章

  1. Nginx与Lua利用fpm打成rpm包
  2. Esfog_UnityShader教程_漫反射DiffuseReflection
  3. DNS服务器分离解析 RAID阵列 进程管理 日志管理 systemd作用
  4. 《Ext JS权威指南》——2.1节获取Ext JS 4
  5. 打印hello world java_java – 如何打印“hello world”?
  6. JQuery AJAX基本使用
  7. 购物车的数据应该保存在哪?
  8. WinForm 中自定义文件与自己的应用程序相关联
  9. gRPC 1.20.1 发布,谷歌开源的高性能 RPC 框架
  10. 2020-08-18 每日一句
  11. MATLAB实现主成分分析
  12. android 音频对比,差距只有安卓?索尼Zx300a与505全方位对比
  13. LogViewer_2
  14. 射线检测C语言,Unity - Raycast 射线检测
  15. 饼图大小调整_PPT制作简约饼图,学会这一个就够了!
  16. 树莓派 arch linux,给树莓派安装ArchLinux
  17. 新出炉彩色游戏——炸弹战争1.0版
  18. Linux中的多路IO转接,转载
  19. 又一年的五一劳动节!
  20. 忻州师范学院2020普通话测试软件,关于2020年普通话测试报名的通知

热门文章

  1. Xv6 traps and system calls
  2. Appium连接教程
  3. 数控车计算机软件编程的重要性,数控车床有多少人软件编程?
  4. 开源阅读书源_【阅读】一款开源的强大的看书软件!amp;超多书源。
  5. 计算机专业英语词汇分类收录
  6. JDBC下载,使用,访问数据库
  7. 国产开源数据可视化套件Cboard使用
  8. 如何配置Modbus读写器
  9. java后台导出word文档正文、表格、图片
  10. sosoapi 项目之本地搭建