zoukankan      html  css  js  c++  java
  • springboot使用@async实现异步线程池

    一 介绍

    工作中经常涉及异步任务,通常是使用多线程技术,比如线程池ThreadPoolExecutor,但使用Executors容易产生OOM,需要手动使用ThreadPoolExecutor创建线程池;在springboot使用 @async 可以实现异步调用,配置线程池参数,可以简单的实现多线程的线程池效果,从而简化开发,避免OOM;

    二 异步调用

    2.1无返回异步

    我们知道同步执行就是按照代码的顺序执行,而异步执行则是无序,在springboot中使用实现异步调用函数非常简单,首先在启动类上加上@EnableAsync 注解;

    /**
     * @Author lsc
     * <p> </p>
     */
    @SpringBootApplication
    @EnableAsync
    public class AsyncRunApp {
    
        public static void main(String[] args) {
            SpringApplication.run(AsyncRunApp.class, args);
        }
    }
    

    其次,在函数上标上@sync注解,表示异步调用

        @Async
        public void taskOne() throws Exception {
            System.out.println("任务一");
    
        }
    
        @Async
        public void taskTwo() throws Exception {
            System.out.println("任务二");
        }
    

    测试代码

        @Autowired
        Task task;
    
        @Test
        public void test() throws Exception {
            task.taskOne();
            task.taskTwo();
        }
    

    如果按照同步执行逻辑会先执行任务一,然后再执行任务二,如果是异步执行,则无序,可能任务一先执行,也可能任务二先执行;

    2.2 有返回值回调

    有时候要知道任务是否执行完成,再继续做其它的业务逻辑,就需要使用到Future接口,其含义是在执行异步任务后会给一个回调函数,我们只要设置回调信息,就可以知道任务是否正确执行完成;我们对异步函数,添加 Future 返回值类型,使用 new AsyncResult<>() 设置回调信息;

    @Component
    public class Task {
    
        @Async
        public Future<String> taskOne() throws Exception {
            System.out.println("任务一");
            return new AsyncResult<>("任务一执行完成");
    
        }
    
        @Async
        public Future<String> taskTwo() throws Exception {
            System.out.println("任务二");
            return new AsyncResult<>("任务二执行完成");
        }
    }
    

    测试代码如下, 等待2个任务全部完成后就打印出返回值信息

     @Autowired
        Task task;
    
        @Test
        public void test() throws Exception {
            Future<String> str1 = task.taskOne();
            Future<String> str2 = task.taskTwo();
            while (true){
                // 如果任务都做完就执行如下逻辑
                if (str1.isDone() && str2.isDone()){
                    System.out.println(str1.get()+":"+str2.get());
                    break;
                }
            }
        }
    

    执行输出

    任务二
    任务一
    任务一执行完成:任务二执行完成
    

    关注公众号 知识追寻者 获取原创PDF,面试题集,最新成熟技术栈

    三 线程池

    在异步掉用中使用的@Async 注解,默认的线程池大小如下;

    # 核心线程数
    spring.task.execution.pool.core-size=8  
    # 最大线程数
    spring.task.execution.pool.max-size=16
    # 空闲线程存活时间
    spring.task.execution.pool.keep-alive=60s
    # 是否允许核心线程超时
    spring.task.execution.pool.allow-core-thread-timeout=true
    # 线程队列数量
    spring.task.execution.pool.queue-capacity=100
    # 线程关闭等待
    spring.task.execution.shutdown.await-termination=false
    spring.task.execution.shutdown.await-termination-period=
    # 线程名称前缀
    spring.task.execution.thread-name-prefix=task-
    

    一般情况下,我们都需要手动创建线程池,使用 ThreadPoolTaskExecutor 类进行配置;这边设置了线程前缀名称,等下测试时就可以判定是否线程池配置成功;

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

    在task类中加上 新的一个方法如下

        @Async
        public void sayHello(String name) {
            LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
        }
    

    使用测试类进行测试

       @Test
        public void testPool() throws Exception {
            task.sayHello("公众号:知识追寻者");
        }
    

    执行结果如下,日志打印出线程名称为zszxz-1;

    有时候,一个项目中如果配置了多个线程池,如下格式

    	@Bean("pool1")
        public TaskExecutor taskExecutor1() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            //....
            return executor;
        }
        @Bean("pool2")
        public TaskExecutor taskExecutor2() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            //....
            return executor;
        }
        @Bean("pool3")
        public TaskExecutor taskExecutor3() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            //....
            return executor;
        }
    

    在使用 @Async注解时就需要指明具体使用的线程池,如下格式

        @Async("pool1")
        public void sayHello1(String name) {
            LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
        }
        
         @Async("pool2")
        public void sayHello1(String name) {
            LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
        }
    

    源码地址:https://github.com/zszxz/study-springboot

  • 相关阅读:
    Pyinstaller打包程序,运行时提示ModuleNotFoundError: No module named ‘pikepdf._cpphelpers’的解决办法
    Tkinter设置askopenfilename通过filetypes指定只能打开某一种格式的文件时,不能打开文件选择器
    Tkinter设置的回调函数程序运行自动执行,点击按钮没有执行回调函数
    写Python爬虫遇到的一些坑
    【Golang】【Lite IDE】Go语言环境安装及开发工具Lite IDE的安装
    VUE--当前页面请求定时器,其他页面不需要
    Cascader 级联选择器-------------子级全选则传父级, 子级未全选则传子级
    数组去重
    上传头像后导航栏中头像同步(Vue中监听sessionStorage)
    ui-app打包创建新证书
  • 原文地址:https://www.cnblogs.com/zszxz/p/14159607.html
Copyright © 2011-2022 走看看