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

 快速了解@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。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024期权交易佣金手续费最低是多少?期权交易有哪些成本?

显性成本 期权交易的显性成本包含期权交易的佣金和交易所费用&#xff0c;分别支付给券商和交易所&#xff0c;统一由券商代收。 佣金 期权佣金是期权交易时支付给券商的费用&#xff0c;佣金通常以交易金额的一定比例计算&#xff0c;可以是固定费用&#xff0c;也可以是滑…

深入理解计算机系统 CSAPP 家庭作业7.12

A:refptr (unsigned)(ADDR(r.symbol) r.addend - refaddr) 0x4004f8 (-4) - 0x4004ea 0xa B:refaddr 0x4004d0 0xa 0x4004da refptr 0x400500 (-4) - 0x4004da 0x22 ​​​​​​​

docker安装与入门使用(适用于小白)

总结&#xff1a;Docker 是一个开源的容器化平台&#xff0c;旨在使开发、部署和运行应用程序的过程更加简单和高效。Docker 使用操作系统级虚拟化在单个主机上运行多个独立的容器。每个容器包含应用程序及其所有依赖项&#xff0c;确保在不同环境中具有一致的运行表现。 下面…

【招聘贴】JAVA后端·唯品会·BASE新加坡

作者|老夏&#xff08;题图&#xff1a;公司业务介绍页&#xff09; “ 请注意&#xff0c;这两个岗是BASE新加坡的&#xff0c;欢迎推荐给身边需要的朋友&#xff08;特别是在新加坡的&#xff09;。” VIP海外业务-产品技术团队&#xff0c;这两个岗位属于后端工程组的岗&…

【ocean】ocnPrin结合getData导出数据

核心就是这一句ocnPrint(?output fout leafValue( getData(“/output” ?result “dc”))) r_list list(4000, 4100, 4200) multi_list list(20,21,22) fout outfile("/home/yourpath/results.txt" "w") foreach(r_value r_listforeach(multi_value …

第11周 多线程接口并行对数据字典的查询优化

第11周 多线程接口并行对数据字典的查询优化 本章概述1. 多线程的初始化方式1.1 简单实现多线程的启动方式(3种)1. 继承Thread实现2. 实现Runnable接口3. 实现callable接口(返回值)1.2 基于线程池实现多线程的启动方式❤❤❤2. 多线程编排工具CompletableFuture2.1 Completable…

onlyoffice实现在单页面加载文档的功能

草图 实现案例的基本原型 这里我们的样式库使用的是Tailwindcss&#xff0c;我们的前端UI组件库使用的是Ant Design Vue。 基本原型是&#xff0c;有个按钮&#xff0c;没有点击按钮的时候&#xff0c;页面显示的时普通的内容。当点击这个按钮的时候&#xff0c;页面加载文档…

UNIAPP编译到微信小程序时,会多一层以组件命名的标签

UNIAPP编译到微信小程序时&#xff0c;会多一层以组件命名的标签 解决方案 可以配置virtualHost来配置 export default {options: {virtualHost: true} }

化茧成蝶 | 继HuggingFace首家落地大模型具身智能场景

关于具身智能的起源 近年来&#xff0c;大语言模型&#xff08;LLMs&#xff09;的兴起给机器人领域带来了革命性的改变&#xff0c;大模型赋予了传统机器人理解和推理的能力&#xff0c;让具身智能这一概念再度出现在大众的视角中。OpenCSG 作为国内 AI 开源社区的先锋&#…

Flask之数据库

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、数据库的分类 1.1、SQL 1.2、NoSQL 1.3、如何选择&#xff1f; 二、ORM魔法 三、使用Flask-SQLALchemy管理数据库 3.1、连接数据库服…

美创携手浙江长征职业技术学院,共建智云数据安全大师工作室

6月24日&#xff0c;“美创科技浙江长征职业技术学院智云数据安全大师工作室揭牌暨中国特色学徒制第四期云数据安全和智能运维人才选拔培养启动仪式”在长征职业技术学院隆重举行。 浙江长征职业技术学院计算机与信息技术学院院长梅灿华、计算机与信息技术学院学工办副主任华春…

[深度学习] Transformer

Transformer是一种深度学习模型&#xff0c;最早由Vaswani等人在2017年的论文《Attention is All You Need》中提出。它最初用于自然语言处理&#xff08;NLP&#xff09;任务&#xff0c;但其架构的灵活性使其在许多其他领域也表现出色&#xff0c;如计算机视觉、时间序列分析…

Orangepi Zero2使用外设驱动库wiringOP配合时间函数驱动HC-SR04超声波测距模块

目录 一、HC-SR04超声波模块原理和硬件接线 1.1 超声波测距原理&#xff1a; 1.2 超声波时序图&#xff1a; 1.3 HC-SR04超声波模块硬件接线&#xff1a; 二、时间函数 2.1 时间函数gettimeofday()原型和头文件&#xff1a; 2.2 使用gettimeofday()函数获取当前时间的秒数…

经验分享,免费商标查询网站

有时候想快速查询商标状况&#xff0c;官方网站比较慢&#xff0c;这里分享一个免费快速的网站。 网址&#xff1a;https://www.sscha.com/ 截图&#xff1a;

platform 设备驱动实验

platform 设备驱动实验 Linux 驱动的分离与分层 代码的重用性非常重要&#xff0c;否则的话就会在 Linux 内核中存在大量无意义的重复代码。尤其是驱动程序&#xff0c;因为驱动程序占用了 Linux内核代码量的大头&#xff0c;如果不对驱动程序加以管理&#xff0c;任由重复的…

电脑没声音是什么原因?一篇文章帮你解决疑惑

在使用电脑时&#xff0c;声音是至关重要的一部分&#xff0c;无论是播放音乐、观看视频还是进行视频会议。然而&#xff0c;有时候电脑可能会出现没声音的情况&#xff0c;这让人感到非常困扰。那么电脑没声音是什么原因呢&#xff1f;本文将详细介绍解决电脑没声音问题的三种…

【Java Web】XML格式文件

目录 一、XML是什么 二、常见配置文件类型 *.properties类型&#xff1a; *.xml类型&#xff1a; 三、DOM4J读取xml配置文件 3.1 DOM4J的使用步骤 3.2 DOM4J的API介绍 一、XML是什么 XML即可扩展的标记语言&#xff0c;由标记语言可知其基本语法和HTML一样都是由标签构成的文件…

仓库管理系统12--物资设置供应商设置

1、添加供应商窗体 2、布局控件UI <UserControl x:Class"West.StoreMgr.View.SupplierView"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc"http://…

VLM系列文章1-LLaVA

作为VLM系列的第一篇文章&#xff0c;打算以LLaVA入手&#xff0c;毕竟是VLM领域较为经典的工作。 1. 核心贡献 多模态指令跟随数据&#xff1a;包括PT、SFT数据的构造流程&#xff1b;大型多模态模型&#xff1a;一种新的框架&#xff0c;结构较为简单高效&#xff1b;评估数…

深入了解 GPT-4 和 ChatGPT 的 API---使用 OpenAI Python 库

文章目录 OpenAI 访问权限和 API 密钥Hello World 示例程序使用 GPT-4 和 ChatGPTChatCompletion 端点的输入选项ChatCompletion 端点的输出格式 OpenAI 将 GPT-4 和 ChatGPT 作为服务提供。这意味着用户无法直接访问模型代码&#xff0c;也无法在自己的服务器上运行这些模型。…