SpringBoot中使用多线程调用异步方法,异步方法有无返回值例子。

news2024/11/20 13:34:08

 快速了解@Async注解的用法,包括异步方法无返回值、有返回值,最后总结@Async注解失效的几个坑。

在我们的 SpringBoot 应用中,经常会遇到在一个接口中,同时做事情1,事情2,事情3,如果同步执行的话,则本次接口时间取决于事情1 2 3执行时间之和;
如果三件事同时执行的话,那么本次接口的运行时间取决于事情1 2 3执行时间最长的那个。合理的使用多线程,可以大大缩短接口的运行时间。
下面几个例子简单的介绍了一下多线程如何调用异步方法的例子。

快速使用

1.创建线程池启用异步调用。

@EnableAsync注解,开启异步调用,异步的方法交给特定的线程池去完成。如下:

package com.lch.multithreadingdemo.config;

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

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * <p>
 * 线程池配置
 * </p>
 *
 * @author Lch
 */
@Configuration
@EnableAsync
public class AsyncConfiguration {
    @Bean("myDoSomethingExecutor")
    public Executor doSomethingExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数:线程池创建时候初始化的线程数
        executor.setCorePoolSize(10);
        // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(20);
        // 缓冲队列:用来缓冲执行任务的队列
        executor.setQueueCapacity(500);
        // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("do-something-");
        // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}
2.使用方法 异步方法无返回值

使用的方式就非常简单了,在需要异步执行的方法上加上@Async注解就可以了。如下:

2.1 Controller:
import com.lch.multithreadingdemo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * <p>
 *
 * </p>
 *
 * @author Lch
 */
@RestController
public class AsyncController {
    @Autowired
    private AsyncService asyncService;

    @GetMapping("/open/mySomething")
    public String something() {
        int count = 10;
        for (int i = 0; i < count; i++) {
            asyncService.doSomething("index = " + i);
        }
        return "success";
    }

}
 2.2 Service:
package com.lch.multithreadingdemo.service.impl;

import com.lch.multithreadingdemo.service.AsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CompletableFuture;

/**
 * <p>
 *
 * </p>
 *
 * @author Lch
 */
@Service
public class AsyncServiceImpl implements AsyncService {

    @Override
    @Async("myDoSomethingExecutor")
    public void doSomething(String message) {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        // 格式化当前时间
        String formattedNow = now.format(formatter);
        // 获取当前线程名
        String threadName = Thread.currentThread().getName();
        System.out.println("时间:  "+formattedNow +"    线程:  " + threadName + "    执行了方法:    " +message);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("do something error: " + e.getMessage());
        }
    }
}
2.3 执行结果:

访问:http://127.0.0.1:8080/open/mySomething 结果如下:

已经达到了异步执行的效果,并且使用到了咱们配置的线程池。 

3.使用方法 异步方法有返回值 
3.1 Controller:
package com.lch.multithreadingdemo.controller;

import com.lch.multithreadingdemo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * <p>
 *
 * </p>
 *
 * @author Lch
 */
@RestController
public class AsyncController {
    @Autowired
    private AsyncService asyncService;

    /**
     * 有返回值
     */
    @GetMapping("/open/mySomethings")
    public String somethings() throws InterruptedException, ExecutionException {
        CompletableFuture<String> doSomething1 = asyncService.doSomething1("第一个方法");
        CompletableFuture<String> doSomething2 = asyncService.doSomething2("第二个方法");
        CompletableFuture<String> doSomething3 = asyncService.doSomething3("第三个方法");
        // 等待所有任务都执行完
        CompletableFuture.allOf(doSomething1, doSomething2, doSomething3).join();
        // 获取每个任务的返回结果
        String result = doSomething1.get() + doSomething2.get() + doSomething3.get();
        return result;
    }
}
3.2 Service:
package com.lch.multithreadingdemo.service.impl;

import com.lch.multithreadingdemo.service.AsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CompletableFuture;

/**
 * <p>
 *
 * </p>
 *
 * @author Lch
 */
@Service
public class AsyncServiceImpl implements AsyncService {

    @Override
    @Async("myDoSomethingExecutor")
    public CompletableFuture<String> doSomething1(String message) throws InterruptedException {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        // 格式化当前时间
        String formattedNow = now.format(formatter);
        String threadName = Thread.currentThread().getName();
        System.out.println("时间:  "+formattedNow +"    线程:  " + threadName + "    执行了方法doSomething1:    " +message);
        Thread.sleep(1000);
        return CompletableFuture.completedFuture("方法doSomething1: "+ message);
    }

    @Override
    @Async("myDoSomethingExecutor")
    public CompletableFuture<String> doSomething2(String message) throws InterruptedException {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        // 格式化当前时间
        String formattedNow = now.format(formatter);
        String threadName = Thread.currentThread().getName();
        System.out.println("时间:  "+formattedNow +"    线程:  " + threadName + "    执行了方法doSomething2:    " +message);
        Thread.sleep(1000);
        return CompletableFuture.completedFuture("方法doSomething2: "+ message);
    }

    @Override
    @Async("myDoSomethingExecutor")
    public CompletableFuture<String> doSomething3(String message) throws InterruptedException {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        // 格式化当前时间
        String formattedNow = now.format(formatter);
        String threadName = Thread.currentThread().getName();
        System.out.println("时间:  "+formattedNow +"    线程:  " + threadName + "    执行了方法doSomething1:    " +message);
        Thread.sleep(1000);
        return CompletableFuture.completedFuture("方法doSomething3: "+ message);
    }
}
3.3 执行结果:

访问:http://127.0.0.1:8080/open/mySomethings 结果如下:

也达到了方法异步执行的效果,并且也拿到了返回值。

注意事项

@Async注解会在以下几个场景失效,也就是说明明使用了注解,但就没有走多线程。

1. 异步方法使用static关键词修饰;

2. 异步类不是一个Spring容器的bean(一般使用注解@Component@Service,并且能被Spring扫描到);

3. SpringBoot应用中没有添加@EnableAsync注解;

4. 在同一个类中,一个方法调用另外一个有@Async注解的方法,注解不会生效。原因是@Async注解的方法,是在代理类中执行的。

异步方法使用注解@Async的返回值只能为void或者Future及其子类,当返回结果为其他类型时,方法还是会异步执行,但是返回值都是null。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1866882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

发电机保护屏产品介绍,组成

发电机保护屏产品介绍&#xff0c;组成 发电机保护屏是用于保护发电机组的电气装置。它根据发电机的类型和实际运行要求&#xff0c;将多种保护装置组合在一起&#xff0c;形成一个保护屏柜。发电机保护测控屏是指把发电机类保护装置集中在安装在一个控制柜里&#xff0c;主要用…

【软件下载】Folx详细安装教程视频-Folx软件最新版下载

根据大数据调查表明Acceleration PRO下载&#xff1a;抽出多达10个流的故障能够显着提高下载速度。根据行业数据显示与iTunes PRO集成&#xff1a;通过将Folx集成到iTunes来下载歌曲和视频&#xff0c;能够在下载后立即自动添加到iTunes库。实际上我们可以这样讲通过代理下载&a…

LabVIEW遇到无法控制国外设备时怎么办

当使用LabVIEW遇到无法控制国外产品的问题时&#xff0c;解决此类问题需要系统化的分析和处理方法。以下是详细的解决思路和具体办法&#xff0c;以及不同方法的分析和比较&#xff0c;包括寻求代理、国外技术支持、国内用过的人请教等内容。 1. 了解产品的通信接口和协议 思路…

小型海外仓布局策略:高效利用有限空间,标准化3F流程

合理高效的仓库空间设计&#xff0c;不只是对大型海外仓很关键。对空间有限的小型海外仓来说或许价值更大。 本身仓储空间就有限&#xff0c;如果还没有科学规划&#xff0c;造成空间浪费&#xff0c;那将直接影响到核心业务的运转。 今天我们就给大家整理了对小型海外仓布局…

制作一个智能体:抖音热点话题文案制作助手

文章目录 第一步&#xff0c;添加助手第二步&#xff0c;选择语聚GPT第三步&#xff0c;填写相关信息第四步&#xff0c;工具中选择抖音(普通号)第五步&#xff0c;选择“查询热门视频数据”第六步&#xff0c;测试总结 这篇文章&#xff0c;我们手把手的演示开发一个智能体&am…

Mysql回表和覆盖索引

一、简述 回表&#xff0c;顾名思义就是回到表中&#xff0c;也就是先通过普通索引扫描出数据所在的行&#xff0c;再通过行主键ID 取出索引中未包含的数据。所以回表的产生也是需要一定条件的&#xff0c;如果一次索引查询就能获得所有的select 记录就不需要回表&#xff0c;…

LLM文本数据集775TB:覆盖32个领域,444个数据集

大语言模型在各领域展现出巨大潜力&#xff0c;其性能在很大程度上依赖于训练和测试所用的数据集。然而&#xff0c;目前在如何构建和优化这些数据集方面&#xff0c;尚缺乏统一的认识和方法论。下面从五个方面整合和分类了LLM数据集的基本内容&#xff1a;预训练语料库、指令微…

在低版本Excel中创建次级下拉列表

在低版本中indirect函数不支持选区&#xff0c;创建次级下拉列表得依靠“名称管理”给选区命名。 (笔记模板由python脚本于2024年06月26日 06:24:22创建&#xff0c;本篇笔记适合常用Excel处理数据的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www…

数据库系统概论(第5版教材)

第一章 绪论 1、数据(Data)是描述事物的符号记录&#xff1b; 2、数据库系统的构成&#xff1a;数据库 、数据库管理系统&#xff08;及其开发工具&#xff09; 、应用程序和数据库管理员&#xff1b; 3、数据库是长期存储在计算机内、有组织、可共享的大量数据的集合&…

linux应用开发基础知识(七)——管道和消息队列进程通信

管道通信 匿名管道 #include <unistd.h> int pipe(int pfd[2]);pfd[0]用于读管道&#xff0c;而pdf[1]用于写管道。 注意&#xff1a;匿名管道只能用于亲缘关系的进程之间通信。管道通道是单向的&#xff0c;一边读&#xff0c;另一边写。管道可以用于大于两个进程共…

基线核查--渗透

基线检查 基线核查概念 it中定义&#xff1a; 基线为初始的标准&#xff0c;以后更改就要经过授权&#xff0c;形成下一基线。 软件配置管理的基线&#xff1a;1功能基线&#xff0c;分配基线&#xff0c;产品基线 安全配置基线--基线核查 安全基线可以说是木桶理论&…

Dubbo运行原理

目录 Dubbo通讯协议 Dubbo负载均衡策略 RPC和HTTP有什么区别&#xff1f; 让你设计一个RPC框架&#xff0c;如何考虑数据序列化问题&#xff1f; Dubbo 是一款高性能、轻量级的开源 RPC&#xff08;远程过程调用&#xff09;框架&#xff0c;主要用于构建分布式服务和微服务…

为什么 Uvicorn 的性能不如 uWSGI?你真的用对了吗?

Uvicorn 简介 Uvicorn 是一个基于 ASGI 的快速 Web 服务器,号称性能赶超 uWSGI。然而,有些人在实际使用中发现 Uvicorn 的性能不如 uWSGI。那么,Uvicorn 真的不如 uWSGI 吗?其实,问题可能出在使用方法上。 Uvicorn 是否真的不如 uWSGI 首先,我们需要了解 Uvicorn 和 uW…

无源电压继电器 JDY-1210AW 导轨安装 约瑟JOSEF

系列型号&#xff1a; JDY-1002AW电压继电器&#xff1b;JDY-1002B电压继电器&#xff1b; JDY-1110AW电压继电器&#xff1b;JDY-1110B电压继电器&#xff1b; JDY-1220AW电压继电器&#xff1b;JDY-1220B电压继电器&#xff1b; JDY-1100AW电压继电器&#xff1b;JDY-110…

浅谈业务开发与非业务开发

浅谈业务开发与非业务开发 软件开发业务开发非业务开发工作量的区别 软件开发 在谈及业务开发与非业务开发之前&#xff0c;首先他们都是软件开发&#xff0c;那么软件开发的流程是怎样的呢&#xff1f;我们先来了解一下软件开发的流程。通常情况下软件开发的流程是这样的 在…

Python中20个鲜为人知的字符串函数

目录 1. capitalize() 2. casefold() 3. join() 和 split() 4. strip(), lstrip(), rstrip() 5. replace() 6. format() 7. enumerate() 8. isalpha(), isdigit(), isalnum() 9. startswith(), endswith() 10. center() 11. count() 12. find(), index() 13. make…

【SQL】数据操作语言(DML) - 删除数据:精细管理数据的利刃

目录 前言 DELETE语句的基础使用 删除指定记录 清空表与删除表数据的区别 注意 前言 在数据库管理的日常工作中&#xff0c;数据的删除是一项需要格外小心的操作&#xff0c;因为一旦数据被删除&#xff0c;往往难以恢复。数据操作语言(DML)中的DELETE语句&am…

MFC---静态文本框和编辑框Edit Control(控件的通知消息)(常用控件)

上一节讲了颜色对话框之后&#xff0c;关于对话框的使用和各种通用对话框的介绍就到此为止了。从本节开始将讲解各种常用控件的用法。常用控件主要包括&#xff1a;静态文本框、编辑框、单选按钮、复选框、分组框、列表框、组合框、图片控件、列表控件、树形控件和进度条控件等…

苹果笔记本双系统怎么安装

想要在mac电脑上装双系统&#xff0c;首先需要确认您的电脑是否支持。苹果电脑自带的boot camp工具可以帮助您在mac上安装windows系统&#xff0c;只需按照步骤进行操作即可。另外&#xff0c;您也可以使用虚拟机软件&#xff0c;如parallels desktop或vmware fusion&#xff0…

uniapp 微信小程序端使用百度地图API

1、登录百度地图开放平台 https://lbsyun.baidu.com/&#xff08;没有账号则先去创建一个百度账号&#xff09; 2、进入百度地图开放平台控制台&#xff08;导航栏“控制台”&#xff09;&#xff0c;点击“应用管理”-“我的应用” 3、选择“创建应用”&#xff0c;应用模块选…