Docker · Spring Boot · Kotlin · 微服务SpringBoot极简教程 · Spring Boot JVM · Java虚拟机原理 · JVM上语言·框架· 生态系统

Spring Boot之多线程、异步:@Async

2020-09-27  本文已影响0人  狄仁杰666

前言

来啦老铁!

笔者学习Spring Boot有一段时间了,附上Spring Boot系列学习文章,欢迎取阅、赐教:

  1. 5分钟入手Spring Boot;
  2. Spring Boot数据库交互之Spring Data JPA;
  3. Spring Boot数据库交互之Mybatis;
  4. Spring Boot视图技术;
  5. Spring Boot之整合Swagger;
  6. Spring Boot之junit单元测试踩坑;
  7. 如何在Spring Boot中使用TestNG;
  8. Spring Boot之整合logback日志;
  9. Spring Boot之整合Spring Batch:批处理与任务调度;
  10. Spring Boot之整合Spring Security: 访问认证;
  11. Spring Boot之整合Spring Security: 授权管理;
  12. Spring Boot之多数据库源:极简方案;
  13. Spring Boot之使用MongoDB数据库源;

近期项目忙碌,家里事情也接踵而至,今天咱简单学点Spring Boot知识:

通常情况下,我们基于Spring Boot写的API或方法,都是同步类型的,同步过程是阻塞式的,前一行代码在未得到结果之前,会产生阻塞,后续的代码就只能等待。比如,调用一个API,该API与数据库交互,然后返回API结果,数据库交互如果用了2秒钟,那么返回API结果这个过程就要等2秒钟,才能发生;

而实际场景中,有些功能其实不需要等待结果就可以执行后续代码,比如:

通常,我们采用多线程技术来实现异步过程,而Spring Boot中,对这个过程又做了简化,使用起来非常简单,接下来我们就一起来探索一下!

项目代码已上传Git Hub仓库,欢迎取阅:

整体步骤

  1. 快速建立Spring Boot项目;
  2. 修饰项目启动类;
  3. 编写Service;
  4. 编写Controller;
  5. 验证效果;
  6. 线程池管理配置类;

1. 快速建立Spring Boot项目;

请参考5分钟入手Spring Boot;

2. 修饰项目启动类;

在项目启动类上添加注解@EnableAsync即可:

package com.github.dylanz666;

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

/**
 * @author : dylanz
 * @since : 09/27/2020
 */
@SpringBootApplication
@EnableAsync
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

3. 编写Service;

为了演示同步与异步的差异,以及异步的不同用法,我会编写3个service类和3个controller类;

1). 同步service类;

package com.github.dylanz666.service;

import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * @author : dylanz
 * @since : 09/27/2020
 */
@Service
public class SyncTaskService {
    public void syncTask1() throws InterruptedException {
        Thread.sleep(2000);//模拟阻塞操作
        System.out.println(new Date() + ": syncTask1 complete.");
    }

    public void syncTask2() throws InterruptedException {
        Thread.sleep(2000);//模拟阻塞操作
        System.out.println(new Date() + ": syncTask2 complete.");
    }

    public void syncTask3() throws InterruptedException {
        Thread.sleep(2000);//模拟阻塞操作
        System.out.println(new Date() + ": syncTask3 complete.");
    }
}

2). 简单的异步service类;

package com.github.dylanz666.service;

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

import java.util.Date;

/**
 * @author : dylanz
 * @since : 09/27/2020
 */
@Service
public class AsyncTaskService {
    @Async
    public void asyncTask1() throws InterruptedException {
        Thread.sleep(2000);//模拟阻塞操作
        System.out.println(new Date() + ": asyncTask1 complete.");
    }

    @Async
    public void asyncTask2() throws InterruptedException {
        Thread.sleep(2000);//模拟阻塞操作
        System.out.println(new Date() + ": asyncTask2 complete.");
    }

    @Async
    public void asyncTask3() throws InterruptedException {
        Thread.sleep(2000);//模拟阻塞操作
        System.out.println(new Date() + ": asyncTask3 complete.");
    }
}

3). 异步拓展应用的service类;

package com.github.dylanz666.service;

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

import java.util.Date;
import java.util.concurrent.Future;

/**
 * @author : dylanz
 * @since : 09/27/2020
 */
@Service
@Async
public class AsyncTaskService2 {
    public Future<String> asyncTask1() throws InterruptedException {
        Thread.sleep(10000);//模拟阻塞操作
        System.out.println(new Date() + ": asyncTask1 complete");
        return new AsyncResult<String>("asyncTask1 complete");
    }

    public Future<String> asyncTask2() throws InterruptedException {
        Thread.sleep(10000);//模拟阻塞操作
        System.out.println(new Date() + ": asyncTask1 complete");
        return new AsyncResult<String>("asyncTask1 complete");
    }

    public Future<String> asyncTask3() throws InterruptedException {
        Thread.sleep(10000);//模拟阻塞操作
        System.out.println(new Date() + ": asyncTask1 complete");
        return new AsyncResult<String>("asyncTask1 complete");
    }
}

简单解读一下:

4. 编写Controller;

1). 用于演示同步过程的API;

package com.github.dylanz666.controller;

import com.github.dylanz666.service.AsyncTaskService;
import com.github.dylanz666.service.SyncTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * @author : dylanz
 * @since : 09/27/2020
 */
@RestController
public class SyncTaskController {
    @Autowired
    private SyncTaskService syncTaskService;

    @GetMapping("/sync/task")
    @ResponseBody
    public String execute() throws InterruptedException {
        long startTimeStamp = System.currentTimeMillis();
        syncTaskService.syncTask1();
        syncTaskService.syncTask2();
        syncTaskService.syncTask3();
        long endTimeStamp = System.currentTimeMillis();
        String message = "sync tasks are complete, duration: " + (endTimeStamp - startTimeStamp) + " ms";
        System.out.println(message);
        return message;
    }
}

2). 用于演示简单异步过程的API;

package com.github.dylanz666.controller;

import com.github.dylanz666.service.AsyncTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * @author : dylanz
 * @since : 09/27/2020
 */
@RestController
public class AsyncTaskController {
    @Autowired
    private AsyncTaskService asyncTaskService;

    @GetMapping("/async/task")
    @ResponseBody
    public String execute() throws InterruptedException {
        long startTimeStamp = System.currentTimeMillis();
        asyncTaskService.asyncTask1();
        asyncTaskService.asyncTask2();
        asyncTaskService.asyncTask3();
        long endTimeStamp = System.currentTimeMillis();
        String message = "async tasks are triggered successfully, duration: " + (endTimeStamp - startTimeStamp) + " ms";
        System.out.println(message);
        return message;
    }
}

3). 用于演示异步拓展应用的API;

package com.github.dylanz666.controller;

import com.github.dylanz666.service.AsyncTaskService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.Future;

/**
 * @author : dylanz
 * @since : 09/27/2020
 */
@RestController
@RequestMapping("/async/complex")
public class AsyncTaskController2 {
    @Autowired
    private AsyncTaskService2 asyncTaskService2;

    public static String status = "async tasks are not triggered.";
    public static Future<String> task1;
    public static Future<String> task2;
    public static Future<String> task3;

    @GetMapping("/task")
    @ResponseBody
    public String execute() throws InterruptedException {
        long startTimeStamp = System.currentTimeMillis();
        task1 = asyncTaskService2.asyncTask1();
        task2 = asyncTaskService2.asyncTask2();
        task3 = asyncTaskService2.asyncTask3();
        long endTimeStamp = System.currentTimeMillis();
        status = "async tasks are doing.";
        String message = "async tasks are triggered successfully, duration: " + (endTimeStamp - startTimeStamp) + " ms";
        System.out.println(message);
        return message;
    }

    @GetMapping("/task/status")
    @ResponseBody
    public String getTasksStatus() {
        assert task1 != null;
        if (task1.isDone() && task2.isDone() && task3.isDone()) {
            status = "async tasks are done.";
        }
        return status;
    }

    @GetMapping("/task/status/{taskId}")
    @ResponseBody
    public Boolean getTaskStatus(@PathVariable(name = "taskId") int taskId) {
        boolean taskStatus = false;
        switch (taskId) {
            case 1:
                taskStatus = task1.isDone();
                break;
            case 2:
                taskStatus = task2.isDone();
                break;
            case 3:
                taskStatus = task3.isDone();
                break;
        }
        return taskStatus;
    }
}

项目整体结构如下:

项目整体结构

5. 验证效果;

启动项目:
启动项目

1). 验证同步API和执行结果;

浏览器直接访问 http://127.0.0.1:8080/sync/task

同步API 同步API log
解读:

2). 验证简单异步API和执行结果;

浏览器直接访问 http://127.0.0.1:8080/async/task

简单异步API 简单异步API log
解读:
3). 验证异步拓展应用API和执行结果;

这里头的API主要演示@Async可以写在类上,可以通过返回Future类型的对象,对异步任务进行处理和获取其信息;

浏览器直接访问 http://127.0.0.1:8080/async/complex/task

调用异步扩展应用API 调用异步扩展应用API log 前10秒内 task状态 前10秒内 单个task状态 10秒后 task状态 10秒后 单个task状态

这样的拓展,我们不仅会使用多线程、异步,而且能够获取异步方法的状态,真香!

6. 线程池管理配置类;

上述这种方式,当并发量很小时,上述方式一般不会有问题,但当并发量很大时,可能会遇到一些问题:
因此,有必要使用线程池对线程进行管理。

通常我们使用Spring提供的ThreadPoolTaskExecutor,进行线程管理;
项目内创建config包,并建立配置类ThreadPoolConfig(名字随意),代码如下:

package com.github.dylanz666.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author : dylanz
 * @since : 09/27/2020
 */
@Configuration
@EnableAsync
public class ThreadPoolConfig {
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //设置核心线程数
        executor.setCorePoolSize(5);
        //设置最大线程数
        executor.setMaxPoolSize(10);
        //设置队列容量
        executor.setQueueCapacity(20);
        //设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        //设置默认线程名称
        executor.setThreadNamePrefix("demo-");
        //设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

如果采用配置类方式管理线程,则项目入口类的@EnableAsync可以去除;
此时项目整体结构:

项目整体结构

至此,我们学会了Spring Boot多线程、异步的基本使用方法,非常简单,相信未来定能派上用场!!!

如果本文对您有帮助,麻烦点赞+关注!

谢谢!

上一篇 下一篇

猜你喜欢

热点阅读