Java异步调用的几种方式

2023-11-14

日常开发中,会经常遇到说,前台调服务,然后触发一个比较耗时的异步服务,且不用等异步任务的处理结果就对原服务进行返回。这里就涉及的Java异步调用的一个知识。下面本文尝试将Java异步调用的多种方式进行归纳。

一、通过创建新线程

首先的我们得认识到,异步调用的本质,其实是通过开启一个新的线程来执行。如以下例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public static void main(String[] args) throws Exception{

    System.out.println("主线程 =====> 开始 =====> " + System.currentTimeMillis());

    new Thread(() -> {

        System.out.println("异步线程 =====> 开始 =====> " + System.currentTimeMillis());

        try{

            Thread.sleep(5000);

        }catch (InterruptedException e){

            e.printStackTrace();

        }

        System.out.println("异步线程 =====> 结束 =====> " + System.currentTimeMillis());

    }).start();

    Thread.sleep(2000);

    System.out.println("主线程 =====> 结束 =====> " + System.currentTimeMillis());

     

}

数据结果如下所示,我们知道,System.currentTimeMillis()时间单位为ms。

主线程 =====> 开始 =====> 1627893837146
异步线程 =====> 开始 =====> 1627893837200
主线程 =====> 结束 =====> 1627893839205
异步线程 =====> 结束 =====> 1627893842212

我们通过线程休眠来达成主线程执行时间2秒左右,异步线程执行5秒左右的效果。通过打印出来的时间戳倒数第四位(秒位)我们可以看出,两个的线程执行总时间为5秒左右,符合异步执行的特征

以上是采用Runable实现多线程创建方式的lambda写法,关于的lambda知识,可参考Java Lambda 表达式;而关于多线程的多种实现方式,Java多线程事务管理一文有提及,可移步查看

二、通过线程池

因为异步任务的实现本质的由新线程来执行任务,所以通过线程池的也可以实现异步执行。写法同我们利用线程池开启多线程一样。但由于我们的目的不是执行多线程,而是异步执行任务,所以一般需要另外一个线程就够了。

因此区别于执行多线程任务的我们常用的newFixedThreadPool,在执行异步任务时,我们用newSingleThreadExecutor 来创建一个单个线程的线程池。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public static void main(String[] args) throws Exception{

    System.out.println("主线程 =====> 开始 =====> " + System.currentTimeMillis());

    ExecutorService executorService = Executors.newSingleThreadExecutor();

    executorService.submit(()->{

        System.out.println("异步线程 =====> 开始 =====> " + System.currentTimeMillis());

        try{

            Thread.sleep(5000);

        }catch (InterruptedException e){

            e.printStackTrace();

        }

        System.out.println("异步线程 =====> 结束 =====> " + System.currentTimeMillis());

    });

    executorService.shutdown(); // 回收线程池

    Thread.sleep(2000);

    System.out.println("主线程 =====> 结束 =====> " + System.currentTimeMillis());

     

}

执行结果如下:

主线程 =====> 开始 =====> 1627895467578
异步线程 =====> 开始 =====> 1627895467635
主线程 =====> 结束 =====> 1627895469644
异步线程 =====> 结束 =====> 1627895472649

可以看到,结果跟第一种结果是基本一致的。

温馨提示:不要忘记线程池的回收

三、通过@Async注解

我们都知道,SpringBoot项目有一个的很重要的特点就是的注解化。如果你的项目是SpringBoot,那就又多了一种选择——@Async注解。

使用起来也非常简单,将要异步执行的代码封装成一个方法,然后用@Async注解该方法,然后在主方法中直接调用就行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@Test

public void mainThread() throws Exception{

    System.out.println("主线程 =====> 开始 =====> " + System.currentTimeMillis());

    collectionBill.asyncThread();

    Thread.sleep(2000);

    System.out.println("主线程 =====> 结束 =====> " + System.currentTimeMillis());

    Thread.sleep(4000); // 用于防止jvm停止,导致异步线程中断

}

@Async

public void asyncThread(){

    System.out.println("异步线程 =====> 开始 =====> " + System.currentTimeMillis());

    try{

        Thread.sleep(5000);

    }catch (InterruptedException e){

        e.printStackTrace();

    }

    System.out.println("异步线程 =====> 结束 =====> " + System.currentTimeMillis());

}

执行结果如下:

主线程 =====> 开始 =====> 1627897539948
异步线程 =====> 开始 =====> 1627897539956
主线程 =====> 结束 =====> 1627897541965
异步线程 =====> 结束 =====> 1627897544966

有以下两点需要注意:

类似@Tranctional注解,@Async注解的方法与调用方法不能在同一个类中,否则不生效
JUnit框架的设计不考虑多线程场景,所以主线程退出后,子线程也会跟着立即退出,所以可以在后面加多线程休眠时间来观察异步线程的执行情况

四、通过CompletableFuture

CompletableFuture是JDK1.8的新特性,是对Future的扩展。CompletableFuture实现了CompletionStage接口和Future接口,增加了异步回调、流式处理、多个Future组合处理的能力。

实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public static void main(String[] args) throws Exception{

    System.out.println("主线程 =====> 开始 =====> " + System.currentTimeMillis());

    ExecutorService executorService = Executors.newSingleThreadExecutor();

    CompletableFuture.runAsync(() ->{

        System.out.println("异步线程 =====> 开始 =====> " + System.currentTimeMillis());

        try{

            Thread.sleep(5000);

        }catch (InterruptedException e){

            e.printStackTrace();

        }

        System.out.println("异步线程 =====> 结束 =====> " + System.currentTimeMillis());

    },executorService);

    executorService.shutdown(); // 回收线程池

    Thread.sleep(2000);

    System.out.println("主线程 =====> 结束 =====> " + System.currentTimeMillis());

     

}

同样可以实现类似的结果如下:

主线程 =====> 开始 =====> 1627898354914
异步线程 =====> 开始 =====> 1627898354977
主线程 =====> 结束 =====> 1627898356980
异步线程 =====> 结束 =====> 1627898359979

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java异步调用的几种方式 的相关文章

  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • Final字段的线程安全

    假设我有一个 JavaBeanUser这是从另一个线程更新的 如下所示 public class A private final User user public A User user this user user public void
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • 禁止的软件包名称:java

    我尝试从数据库名称为 jaane 用户名 Hello 和密码 hello 获取数据 错误 java lang SecurityException Prohibited package name java at java lang Class
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • getResourceAsStream() 可以找到 jar 文件之外的文件吗?

    我正在开发一个应用程序 该应用程序使用一个加载配置文件的库 InputStream in getClass getResourceAsStream resource 然后我的应用程序打包在一个 jar文件 如果resource是在里面 ja
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • AWS 无法从 START_OBJECT 中反序列化 java.lang.String 实例

    我创建了一个 Lambda 函数 我想在 API 网关的帮助下通过 URL 访问它 我已经把一切都设置好了 我还创建了一个application jsonAPI Gateway 中的正文映射模板如下所示 input input params
  • 如何从终端运行处理应用程序

    我目前正在使用加工 http processing org对于一个小项目 但是我不喜欢它附带的文本编辑器 我使用 vim 编写所有代码 我找到了 pde 文件的位置 并且我一直在从 vim 中编辑它们 然后重新打开它们并运行它们 重新加载脚
  • simpleframework,将空元素反序列化为空字符串而不是 null

    我使用简单框架 http simple sourceforge net http simple sourceforge net 在一个项目中满足我的序列化 反序列化需求 但在处理空 空字符串值时它不能按预期工作 好吧 至少不是我所期望的 如
  • 编译器抱怨“缺少返回语句”,即使不可能达到缺少返回语句的条件

    在下面的方法中 编译器抱怨缺少退货声明即使该方法只有一条路径 并且它包含一个return陈述 抑制错误需要另一个return陈述 public int foo if true return 5 鉴于Java编译器可以识别无限循环 https
  • 有没有办法为Java的字符集名称添加别名

    我收到一个异常 埋藏在第 3 方库中 消息如下 java io UnsupportedEncodingException BIG 5 我认为发生这种情况是因为 Java 没有定义这个名称java nio charset Charset Ch
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List

随机推荐

  • 抖音矩阵系统功能开发及开发文档说明

    抖音账号矩阵系统开发文档是矩阵系统开发的重要文件 涵盖了矩阵系统的设计 实现 测试和维护等方面的相关信息 这些信息是矩阵系统开发人员进行系统开发的重要参考 有助于确保系统开发的顺利进行和最终的实现效果 矩阵系统开发文档一般包括以下几个方面的
  • mysql的悲观锁和乐观锁

    悲观锁 悲观锁指的是对数据被外界 包括本系统当前的其他事务 以及来自外部系统的事务处理 修改持保守态度 因此 在整个数据处理过程中 将数据处于锁定状态 悲观锁的实现 往往依靠数据库提供的锁机制 也只有数据库层提供的锁机制才能真正保证数据访问
  • MyBatis框架的作用?

    1 MyBatis 是一个优秀的基于 java 的持久层框架 它内部封装了 jdbc 使开发者只需要关注 sql 语句本身 而不需要花费精力去处理加载驱动 创建连接 创建 statement 等繁杂的过程 2 MyBatis为了和数据库进行
  • C++函数(详细版)

    函数 函数指针 内联函数 decltype关键字 atuo关键字 返回引用 const关键字 二维数组 函数指针 概念 函数也是有地址的 而指向这个地址的指针就是函数指针 1 获取函数的地址 使用函数名即可 例如think 是一个函数 则t
  • Python编程挑战赛

    题1 给小朋友分糖 每人分到糖的数量不同 输入小朋友的数量 计算至少需要多少糖 思路 第1个小朋友1颗糖 第2个小朋友2颗糖 第3个小朋友3颗糖 第n个小朋友n颗糖 计算1 2 n的和即可 第1种写法 不用Python高级函数 n int
  • 安装和简单使用visual studio 2017

    1 安装visual studio installer小程序 VS 2017社区版 Community 下载地址 百度网盘下载链接 百度网盘 请输入提取码 密码 ub6c 2 在visual studio installer里下载安装vis
  • Vue3 自定义指令

    在前端项目中 有很多需求是需要在多页面进行逻辑处理 通常我们所需要的功能可以通过DOM操作来实现 或者多处功能一致 我们就可以使用自定义指令 在vue2中的自定义指令指令注册的方法是 v focus js import vue from v
  • 概率论之 -- 边缘分布

    边缘分布 Marginal Distribution 指在概率论和统计学的多维随机变量中 只包含其中部分变量的概率分布 中文名 边缘分布 外文名 marginal distribution 又 名 边际分布 应用学科 概率论 统计学 定义
  • 还在用夸克?这3款能安装插件的手机浏览器不香吗

    说到浏览器插件 很多人想到的多数是电脑上的玩法 实际上 随着手机浏览器功能越来越完善 很多手机浏览器已经开始支持插件的使用 也就是说 支持安装插件的手机浏览器 不仅能体验如电脑般丝滑强大的功能 而且又不会造成内存过分臃肿 开启响应缓慢的问题
  • GLSL着色器的正确文件扩展名是什么?

    openGL系列文章目录 文章目录 openGL系列文章目录 前言 一 glslangValidator exe工具使用 二 着色器程序后缀名 前言 我正在学习glsl着色 我遇到了不同的文件格式 我见过人们给出他们的顶点和片段着色器 ve
  • Java中在特定区间产生随机数

    原文地址 http blog sina com cn s blog 59aebaa10100ct47 html 参考地址 http blog csdn net codefunjava article details 44408555 htt
  • 贝叶斯分类器-机器学习ML

    参考 1 统计学习方法 李航 2 https baike baidu com item E8 B4 9D E5 8F B6 E6 96 AF E5 88 86 E7 B1 BB E5 99 A8 1739590 fr aladdin 3 h
  • 力扣|错误的集合 C语言

    题目连接 错误的集合 集合 s 包含从 1 到 n 的整数 不幸的是 因为数据错误 导致集合里面某一个数字复制了成了集合里面的另外一个数字的值 导致集合 丢失了一个数字 并且 有一个数字重复 给定一个数组 nums 代表了集合 S 发生错误
  • 浅析muduo库中的定时器设施

    一个设计良好的定时器在服务端的应用程序上至关重要 muduo定时器的实现陈硕大牛在书中已经详细的谈过 笔者尝试从源码的角度解读定时器的实现 如果理解不对 欢迎指正 在muduo的定时器系统中 一共由四个类 Timestamp Timer T
  • 学习CSSGrid布局

    一 重要术语 CSS Grid 网格 布局 又称为 Grid 网格 是一个二维的基于网格的布局系统 它的目标是完全改变我们基于网格的用户界面的布局方式 FlexBox 一维布局 Grid 二维布局 Flexbox 和 Grid 能协同工作
  • C# 文件IO

    文章目录 判断某个文件夹是否存在 获取当前运行程序 exe或dll 所在路径 创建文件夹 移动 剪切 文件夹 复制文件 创建文件 覆盖写文件 方式一 使用FileStream 方式二 使用StreamWriter 追加写文件 读文件 一 一
  • springBoot国际化的一种方式

    引言 当我们的应用面向不同国家用户时 根据不同的locale返回不同的语言信息的国际化功能就显得有必要了 一般来说国际化主要表现在前端用户界面上 在现在前后端分离的背景下 前端页面的国际化交由前端代码独立完成 少部分表现在后端上 后端主要表
  • HTML存储详解

    和大家一起先来了解一下H5之前的存储方式 cookies的诞生 http请求头上带着数据 大小只能为4K 主Domain的污染 下面是百度的一些Cookies HTTP中带 的表示 只能被服务器端修改的数据 一般用来存储身份验证等信息 co
  • 搞清axis的含义,这一篇就够了!

    文章目录 axis的含义 旁门左道式理解 二维数组中的axis 三维数组中的axis 正规理解 axis的含义 在自己分析之前先摆上官方关于多维数组中axis的值的定义 axis 0 表示第一个维度 axis 1 表示第二个维度 axis
  • Java异步调用的几种方式

    一 通过创建新线程 二 通过线程池 三 通过 Async注解 四 通过CompletableFuture 日常开发中 会经常遇到说 前台调服务 然后触发一个比较耗时的异步服务 且不用等异步任务的处理结果就对原服务进行返回 这里就涉及的Jav