自学内容网 自学内容网

SpringBoot - Async异步处理

目录

一、定义

1、同步调用

2、异步调用

二、示例

1、同步调用

执行类:

测试用例: 

运行结果:

2、异步调用

(1)普通调用

执行类:

测试用例:

运行结果:

(2)异步回调

执行类:

测试用例:

执行逻辑:


一、定义

1、同步调用

  • 定义同步调用是一种按照顺序依次执行任务的方式。在程序中,当一个函数或方法调用另一个函数或方法时,调用者会暂停自身的执行,等待被调用者执行完毕并返回结果后,调用者才会继续执行后续的操作。
  • 就好像一个人在排队等待服务,必须等到前面的人完成服务后,自己才能接受服务并继续下一步行动。

2、异步调用

  • 定义异步调用是一种不阻塞调用者执行的调用方式。当一个函数或方法被异步调用时,调用者不会等待被调用者执行完毕,而是继续执行自己后续的操作。被调用者在后台独立运行,当它完成任务后,会通过某种方式(如回调函数、事件通知等)将结果返回给调用者。
  • 这就好比一个人在餐厅点了菜后,不需要一直坐在那里等待上菜,而是可以去做其他事情,等菜做好了,服务员会通知他。

二、示例

1、同步调用

执行类:

        定义SynchronizationTask类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内)

package com.example.springbootasync.demos.web;

import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * @Author: 
 * @CreateTime: 2024-11-15
 * @Description: 同步任务执行类
 */
@Component
public class SynchronizationTask {

    public static Random random = new Random();

    public void taskOne() throws Exception {
        System.out.println("任务一开始执行......");

        long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();

        System.out.println("任务一完成耗时:" + (end - start) + "毫秒");
    }

    public void taskTwo() throws Exception {
        System.out.println("任务二开始执行......");

        long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();

        System.out.println("任务二完成耗时:" + (end - start) + "毫秒");
    }

    public void taskThree() throws Exception {
        System.out.println("任务三开始执行......");

        long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();

        System.out.println("任务三完成耗时:" + (end - start) + "毫秒");
    }

}
 测试用例: 

          在单元测试用例中,注入SynchronizationTask对象,并在测试用例中执行taskOne、taskTwo、taskThree三个函数。

package com.example.springbootasync;

import com.example.springbootasync.demos.web.SynchronizationTask;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringBootAsyncApplicationTests {

    @Autowired
    private SynchronizationTask sTask;

    @Test
    void synchronizationTask() throws Exception {
        sTask.taskOne();
        sTask.taskTwo();
        sTask.taskThree();
    }

}

执行单元测试

运行结果:

任务一开始执行......
任务一完成耗时:2649毫秒
任务二开始执行......
任务二完成耗时:7083毫秒
任务三开始执行......
任务三完成耗时:5752毫秒

        可以看出三个任务是按照顺序执行的,这就是同步调用,一个任务执行完成才能执行下一个任务。

2、异步调用

        上述的同步调用虽然顺利的执行完了三个任务,但是可以看到执行时间比较长,若这三个任务本身之间不存在依赖关系,可以并发执行的话,同步调用在执行效率方面就比较差,可以考虑通过异步调用的方式来并发执行。

       为了让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsync,如下所示:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class SpringBootAsyncApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootAsyncApplication.class, args);
    }

}

(1)普通调用

        在Spring Boot中,我们只需要通过使用 @Async注解 就能简单的将原来的 同步函数变为异步函数,将SynchronizationTask类进行改造,如下:

执行类:
package com.example.springbootasync.demos.web;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.Future;

/**
 * @Author: wxzhanglinsen
 * @CreateTime: 2024-11-15
 * @Description: 异步任务执行类
 */
@Component
public class AsyncTask {

    public static Random random = new Random();

    @Async
    public void taskOne() throws Exception {
        //同上
    }

    @Async
    public void taskTwo() throws Exception {
        //同上
    }

    @Async
    public void taskThree() throws Exception {
        //同上
    }

}
测试用例:
package com.example.springbootasync;

import com.example.springbootasync.demos.web.AsyncTask;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringBootAsyncApplicationTests {

    @Autowired
    private AsyncTask aTask;

    @Test
    void asyncTask() throws Exception {
        aTask.taskOne();
        aTask.taskTwo();
        aTask.taskThree();
    }

}
运行结果:

任务二开始执行......
任务三开始执行......
任务一开始执行......

任务二开始执行......
任务一开始执行......
任务三开始执行......

此时可以反复执行单元测试,您可能会遇到各种不同的结果,比如:

  • 没有任何任务相关的输出

  • 有部分任务相关的输出

  • 乱序的任务相关的输出

        原因是目前taskOne、taskTwo、taskThree三个函数的时候已经是异步执行了。主程序在异步调用之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束了,导致了不完整或是没有输出任务相关内容的情况。

注意:@Async所修饰的函数不要定义为static类型,这样异步调用不会生效。

(2)异步回调

        为了让taskOne、taskTwo、taskThree能正常结束,假设我们需要统计一下三个任务并发执行共耗时多少,这就需要等到上述三个函数都完成调动之后记录时间,并计算结果。

        那么我们如何判断上述三个异步调用是否已经执行完成呢?我们需要使用Future来返回异步调用的结果,就像如下方式改造AsyncTask类中的函数:

 执行类:
package com.example.springbootasync.demos.web;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.Future;

/**
 * @Author: wxzhanglinsen
 * @CreateTime: 2024-11-15
 * @Description: 异步任务执行类
 */
@Component
public class AsyncTask {

    public static Random random = new Random();

    @Async
    public Future<String> taskOne() throws Exception {
        System.out.println("任务一开始执行......");

        long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();

        System.out.println("任务一完成耗时:" + (end - start) + "毫秒");

        return new AsyncResult<>("任务一完成");
    }

    @Async
    public Future<String> taskTwo() throws Exception {
        System.out.println("任务二开始执行......");

        long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();

        System.out.println("任务二完成耗时:" + (end - start) + "毫秒");

        return new AsyncResult<>("任务二完成");
    }

    @Async
    public Future<String> taskThree() throws Exception {
        System.out.println("任务三开始执行......");

        long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();

        System.out.println("任务三完成耗时:" + (end - start) + "毫秒");

        return new AsyncResult<>("任务三完成");
    }
}
测试用例:
package com.example.springbootasync;

import com.example.springbootasync.demos.web.AsyncTask;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.Future;

@SpringBootTest
class SpringBootAsyncApplicationTests {

    @Autowired
    private AsyncTask aTask;

    @Test
    void asyncTask() throws Exception {
        long start = System.currentTimeMillis();

        Future<String> task1 = aTask.taskOne();
        Future<String> task2 = aTask.taskTwo();
        Future<String> task3 = aTask.taskThree();

        while(true) {

            if(task1.isDone() && task2.isDone() && task3.isDone()) {
                // 三个任务都调用完成,退出循环等待
                break;
            }

            //停隔1s再进行扫描,扫描任务是否都已经完成
            Thread.sleep(1000);
        }

        long end = System.currentTimeMillis();

        System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
    }
}
执行逻辑:
  • 在测试用例一开始记录开始时间

  • 在调用三个异步函数的时候,返回Future类型的结果对象

  • 在调用完三个异步函数之后,开启一个循环,根据返回的Future对象来判断三个异步函数是否都结束了。若都结束,就结束循环;若没有都结束,就等1秒后再判断。

跳出循环之后,根据结束时间 - 开始时间,计算出三个任务并发执行的总耗时。

运行结果:

任务一开始执行......
任务二开始执行......
任务三开始执行......
任务一完成耗时:188毫秒
任务二完成耗时:4828毫秒
任务三完成耗时:9208毫秒
任务全部完成,总耗时:10014毫秒

 可以看到,通过异步调用,让任务一、二、三并发执行,有效的减少了程序的总运行时间。


原文地址:https://blog.csdn.net/Moon_Light0/article/details/143801375

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!