说说Java异步调用的几种方式

目录
  • 一、通过创建新线程
  • 二、通过线程池
  • 三、通过@Async注解
  • 四、通过CompletableFuture

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

一、通过创建新线程

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

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 来创建一个单个线程的线程池。

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注解该方法,然后在主方法中直接调用就行。

@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组合处理的能力。

实现代码如下:

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

CompletableFuture有者非常强大的功能,能给我们带来非常丝滑的编程体验。后续会写一篇文章来详细介绍CompletableFuture

到此这篇关于说说Java异步调用的几种方式的文章就介绍到这了,更多相关Java异步调用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JAVA实现异步调用实例代码

    在JAVA平台,实现异步调用的角色有如下三个角色: 调用者 取货凭证   真实数据 一个调用者在调用耗时操作,不能立即返回数据时,先返回一个取货凭证.然后在过一断时间后凭取货凭证来获取真正的数据. 在调用一个方法的时候,程序会进入被调用方法体内,执行完这个被调用方法后,才返回执行下一条语句.怎么做到像ajax异步请求一样,发送请求后,没等请求响应就执行下一条语句呢?对于java的异步请求,找了许多教材都没有找到,如thinking in java.core java2 ......等等.受多线程

  • Java接口异步调用

    前言 java接口调用从调用方式上可以分为3类:同步调用,异步调用,回调:同步调用基本不用说了,它是一种阻塞式的调用,就是A方法中直接调用方法B,从上往下依次执行.今天来说说异步调用. 什么是异步调用? 我的理解就是在方法A中调用方法B,但是方法B很耗时,如果是同步调用的话会等方法B执行完成后才往下执行,如果异步的话就是我调用了方法B,它给我个返回值证明它已接受调用,但是它并没有完成任务,而我就继续往下执行.可能有表达不当的地方,欢迎指正. 再用一个生活中的例子吧: 异步调用就是你 喊 你朋友吃

  • Java异步调用转同步方法实例详解

    先说一下对异步和同步的理解: 同步调用:调用方在调用过程中,持续等待返回结果. 异步调用:调用方在调用过程中,不直接等待返回结果,而是执行其他任务,结果返回形式通常为回调函数. 其实,两者的区别还是很明显的,这里也不再细说,我们主要来说一下Java如何将异步调用转为同步.换句话说,就是需要在异步 调用过程中,持续阻塞至获得调用结果. 不卖关子,先列出五种方法,然后一一举例说明: 使用wait和notify方法 使用条件锁 Future 使用CountDownLatch 使用CyclicBarri

  • 5种必会的Java异步调用转同步的方法你会几种

    Sunny先来说一下对异步和同步的理解: 同步调用:调用方在调用过程中,持续等待返回结果. 异步调用:调用方在调用过程中,不直接等待返回结果,而是执行其他任务,结果返回形式通常为回调函数. 其实,两者的区别还是很明显的,这里也不再细说,我们主要来说一下Java如何将异步调用转为同步.换句话说,就是需要在异步调用过程中,持续阻塞至获得调用结果. 不卖关子,先列出五种方法,然后一一举例说明: 使用wait和notify方法 使用条件锁 Future 使用CountDownLatch 使用Cyclic

  • Java异步调用转同步的方法

    先来说一下对异步和同步的理解: 同步调用:调用方在调用过程中,持续等待返回结果. 异步调用:调用方在调用过程中,不直接等待返回结果,而是执行其他任务,结果返回形式通常为回调函数. 其实,两者的区别还是很明显的,这里也不再细说,我们主要来说一下Java如何将异步调用转为同步.换句话说,就是需要在异步调用过程中,持续阻塞至获得调用结果. 不卖关子,先列出五种方法,然后一一举例说明: 使用wait和notify方法 使用条件锁 Future 使用CountDownLatch 使用CyclicBarri

  • Java多线程实现异步调用的方法

    在JAVA平台,实现异步调用的角色有如下三个角色:调用者 提货单   真实数据 一个调用者在调用耗时操作,不能立即返回数据时,先返回一个提货单.然后在过一断时间后凭提货单来获取真正的数据. 去蛋糕店买蛋糕,不需要等蛋糕做出来(假设现做要很长时间),只需要领个提货单就可以了(去干别的事情),等到蛋糕做好了,再拿提货单取蛋糕就可以了. public class Main { public static void main(String[] args) { System.out.println("ma

  • 说说Java异步调用的几种方式

    目录 一.通过创建新线程 二.通过线程池 三.通过@Async注解 四.通过CompletableFuture 日常开发中,会经常遇到说,前台调服务,然后触发一个比较耗时的异步服务,且不用等异步任务的处理结果就对原服务进行返回.这里就涉及的Java异步调用的一个知识.下面本文尝试将Java异步调用的多种方式进行归纳. 一.通过创建新线程 首先的我们得认识到,异步调用的本质,其实是通过开启一个新的线程来执行.如以下例子: public static void main(String[] args)

  • Java 异步实现的几种方式小结

    Java 异步实现的几种方式 1. jdk1.8之前的Future jdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象,可以通过future获得执行的结果,但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询. 通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前是不支持的,但是我们可以通过第三方的库实现真正的异步回调. /** * jdk1.8之前的Future

  • java异步调用的4种实现方法

    目录 一.利用多线程 直接new线程 使用线程池 二.采用Spring的异步方法去执行(无返回值) @Async注解可以用在方法上,也可以用在类上,用在类上,对类里面所有方法起作用 三.采用Spring的异步方法+Future接收返回值 如果调用之后接收返回值,不对返回值进行操作则为异步操作,进行操作则转为同步操作,等待对返回值操作完之后,才会继续执行主进程下面的流程 四.原生Future方法 参考 一.利用多线程 直接new线程 Thread t = new Thread(){ @Overri

  • jQuery 中ajax异步调用的四种方式

    <script type="text/javascript" language="javascript" src="JS/jquery-1[1].2.3.min.js"></script> <script type="text/javascript" language="javascript"> $(document).ready(function(){ //Jquery

  • java异步编程的7种实现方式小结

    目录 同步编程 一.线程 Thread 二.Future 三.FutureTask 四.异步框架 CompletableFuture 五. SpringBoot 注解 @Async 六.Spring ApplicationEvent 事件 七.消息队列 最近有很多小伙伴给我留言,能不能总结下异步编程,今天就和大家简单聊聊这个话题. 早期的系统是同步的,容易理解,我们来看个例子 同步编程 当用户创建一笔电商交易订单时,要经历的业务逻辑流程还是很长的,每一步都要耗费一定的时间,那么整体的RT就会比较

  • Java创建多线程的几种方式实现

    1.继承Thread类,重写run()方法 //方式1 package cn.itcats.thread.Test1; public class Demo1 extends Thread{ //重写的是父类Thread的run() public void run() { System.out.println(getName()+"is running..."); } public static void main(String[] args) { Demo1 demo1 = new D

  • Java单例模式实现的几种方式

    Java单例模式实现的几种方式 单例模式好多书上都是这么写的: public class SingleTon1 { private SingleTon1(){ } private static SingleTon1 instance = null; public static SingleTon1 getInstance(){ if(instance == null){ instance = new SingleTon1(); } return instance; } } 但是实际开发中是不会这

  • 详解Java实现多线程的三种方式

    本文实例为大家分享了Java实现多线程的三种方式,供大家参考,具体内容如下 import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) { //方法一:继承Thread int i = 0; // for(; i < 100; i++){ // System.out.println(T

  • Java 实现多线程的几种方式汇总

    我们先来看段示例代码 import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) { //方法一:继承Thread int i = 0; // for(; i < 100; i++){ // System.out.println(Thread.currentThread().getNa

随机推荐