SpringBoot实现Async异步任务

开课吧樵夫2021-09-02 14:18

Spring提供了Async注解来实现方法的异步调用。当调用Async标识的方法时,调用线程不会等待被调用方法执行完成即返回继续执行以下操作,而被调用的方法则会启动一个独立线程来执行此方法。

SpringBoot实现Async异步任务

如果想实现Async异步任务,首先需要配置三个任务方法用来调用:

taskOne()、taskTwo()、taskTree()

package com.javafamily.familydemo.task;

import java.util.Random;

import static java.lang.System.currentTimeMillis;
import static java.lang.Thread.sleep;

public class TaskMethodProvider {
    private static Random random = new Random();

    public void taskOne() throws Exception {
        System.out.println("开始做任务一");
        long start = currentTimeMillis();
        sleep(random.nextInt(10000));
        long end = currentTimeMillis();
        System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    public void taskTwo() throws Exception {
        System.out.println("开始做任务二");
        long start = currentTimeMillis();
        sleep(random.nextInt(10000));
        long end = currentTimeMillis();
        System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    public void taskThree() throws Exception {
        System.out.println("开始做任务三");
        long start = currentTimeMillis();
        sleep(random.nextInt(10000));
        long end = currentTimeMillis();
        System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
    }
}

并在SpringBoot启动类上添加 EnableAsync注解。

package com.javafamily.familydemo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@MapperScan(basePackages = {"com.javafamily.familydemo.mapper"})
// 扫描原生Servlet位置(监听器是Servlet规范中定义的一种特殊类)
@ServletComponentScan
@EnableAsync
public class FamilyDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(FamilyDemoApplication.class, args);
    }
}

SpringBoot实现Async异步任务

同步调用

下面我们来通过代码,演示一下同步调用。

定义一个类继承TaskMethodProvider:

package com.javafamily.familydemo.task;

import org.springframework.stereotype.Component;

@Component
public class SyncTask extends TaskMethodProvider {
}

之后在单元测试中,注入SyncTask对象,并在测试用例中执行三个方法:

package com.javafamily.familydemo;

import com.javafamily.familydemo.task.SyncTask;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class Task {
    @Autowired
    private SyncTask task;

    @Test
    public void testSyncTasks() throws Exception {
        task.taskOne();
        task.taskTwo();
        task.taskThree();
    }
}

执行单元测试,得到输出结果:

SpringBoot实现Async异步任务

从输出的结果可以看出,任务一、二、三是按照顺序执行完成的。

异步调用

使用同步调用虽然能够完成任务,但是调用时间偏长。如果这三个任务之间不存在依赖关系,能够并行的话,那效率也可以得到很大的提高。

创建AsyncTask类,在方法上配置 Async注解,将原来的同步方法变为异步方法:

package com.javafamily.familydemo.task;

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

@Component
public class AsyncTask extends TaskMethodProvider {
    @Async
    public void taskOne() throws Exception {
        super.taskOne();
    }

    @Async
    public void taskTwo() throws Exception {
        super.taskTwo();
    }

    @Async
    public void taskThree() throws Exception {
        super.taskThree();
    }
}

改写单元测试:

package com.javafamily.familydemo;

import com.javafamily.familydemo.task.AsyncTask;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class Task {

    @Autowired
    private AsyncTask task;

    @Test
    public void testAsyncTasks() throws Exception {
        task.taskOne();
        task.taskTwo();
        task.taskThree();
    }

}

执行单元测试,可能会遇到各种不同的结果:

没有任务相关输出

有部分任务相关输出

有错乱顺序的任务相关输出

产生这样的结果是因为三个方法已经异步并发执行,异步调用之后,主程序不会理会三个方法是否执行完成,如果没有其他需要执行的内容,程序就自动结束了,导致了任务不完整或是没有输出相关内容的情况。

异步回调

从上面的例子可以看出,异步调用不能让三个方法正常结束。如果想要他们正常结束并统计并发执行所消耗的时长,我们可以使用Future<T>来返回异步调用的结果。

创建CallBackTask类,对原有的三个方法进行包装:

package com.javafamily.familydemo.task;

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

import java.util.concurrent.Future;

@Component
public class CallBackTask extends TaskMethodProvider {
    @Async
    public Future<String> doTaskOneCallback() throws Exception {
        super.TaskOne();
        return new AsyncResult<>("结束任务一");
    }

    @Async
    public Future<String> doTaskTwoCallback() throws Exception {
        super.TaskTwo();
        return new AsyncResult<>("结束任务二");
    }

    @Async
    public Future<String> doTaskThreeCallback() throws Exception {
        super.TaskThree();
        return new AsyncResult<>("结束任务三");
    }
}

在单元测试中使用Future的isDone()方法等待三个并发任务执行完成,记录最终执行时间:

package com.javafamily.familydemo;

import com.javafamily.familydemo.task.CallBackTask;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.Future;

import static java.lang.System.currentTimeMillis;
import static java.lang.Thread.sleep;

@RunWith(SpringRunner.class)
@SpringBootTest
public class Task {

    @Autowired
    private CallBackTask task;

    @Test
    public void testAsyncCallbackTask() throws Exception {
        long start = currentTimeMillis();
        Future<String> task1 = task.doTaskOneCallback();
        Future<String> task2 = task.doTaskTwoCallback();
        Future<String> task3 = task.doTaskThreeCallback();

        // 三个任务都调用完成,退出循环等待
        while (!task1.isDone() || !task2.isDone() || !task3.isDone()) {
            sleep(1000);
        }

        long end = currentTimeMillis();
        System.out.println("耗时:" + (end - start) + "毫秒");
    }


}

执行单元测试,得到打印结果:

SpringBoot实现Async异步任务

代码在task1开始时,记录开始时间。调用三个异步函数时,返回Future类型的结果对象。在调用完三个异步函数之后,开始循环,根据返回的Future对象来判断三个异步函数是否都结束了。

都结束了——退出循环

没全结束——等1秒后再判断

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

异步调用让三个方法并行,减少了总运行时间。

以上就是开课吧小编为大家整理发布的“SpringBoot实现Async异步任务”一文,更多相关内容尽在开课吧Java教程频道。

SpringBoot实现Async异步任务

免责声明:本站所提供的内容均来源于网友提供或网络搜集,由本站编辑整理,仅供个人研究、交流学习使用。如涉及版权问题,请联系本站管理员予以更改或删除。
有用
分享
全部评论快来秀出你的观点
登录 后可发表观点…
发表
暂无评论,快来抢沙发!
高并发编程训练营