• Spring Boot注解之@Async和自定义线程池


    前言

      我们在学习线程池的时候,都知道线程池的核心线程数、最大线程数、线程工厂等核心参数非常重要,故熟记于心。但是有些工作五六年的攻城狮可能说不出来怎么初始化一个全局线程池,以在不同场景使用;所以,本文基于Spring Boot的异步注解@Async自定义全局线程池。如果是这方面的老铁,请绕道而行,这里都是雕虫小技。

    maven坐标

            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.7</version>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>31.0.1-jre</version>
            </dependency>
    

    自定义线程池

      核心线程数和最大线程数等参数可以在配置文件中定义,这里为了简单,就直接赋值了。

    import com.google.common.util.concurrent.ThreadFactoryBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.concurrent.*;
    
    /**
     * @Author: Wiener
     * @Date: 2022/1/8 19:57
     */
    @Configuration
    public class AsyncTaskConfig  {
    
        // 包括corePoolSize线程在内,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁
        @Bean("first-executor")
        public Executor firstExecutor() {
            ThreadFactory guavaFactory = new ThreadFactoryBuilder().setNameFormat("first-executor").build();
            int curSystemThreads = Runtime.getRuntime().availableProcessors() * 2;
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(curSystemThreads, 32,
                    30L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(), guavaFactory);
            threadPool.allowsCoreThreadTimeOut();
            return threadPool;
        }
        //my-task用于指定线程池名字
        @Bean("my-executor")
        public Executor myExecutor() {
            ThreadFactory guavaFactory = new ThreadFactoryBuilder().setNameFormat("my-executor").build();
            int curSystemThreads = Runtime.getRuntime().availableProcessors() * 2;
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(curSystemThreads, 32,
                    30L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(), guavaFactory);
            threadPool.allowsCoreThreadTimeOut();
            return threadPool;
        }
    
    }
    

    使用@Async注解创建线程任务

      Spring为任务调度与异步方法执行提供了注解@Async支持,通过在方法上标注@Async注解,可使得方法被异步调用。在需要异步执行的方法上加入@Async注解,并指定使用的线程池,当然可以不指定,直接写@Async。

    service定义如下:

    /**
     *  指定pool
     * @Author: Wiener
     * @Date: 2022/1/8 19:59
     */
    public interface AsyncTaskService {
        void executeAsyncTask(Integer i);
        void executeMyTask(Integer i);
    }
    

    实现类如下:

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Service;
    
    /**
     * @Author: Wiener
     * @Date: 2022/1/9 12:33
     */
    @Slf4j
    @Service
    public class AsyncTaskServiceImpl implements AsyncTaskService {
    
        @Async("first-executor")
        @Override
        public void executeAsyncTask(Integer i) {
            System.out.println("线程" + Thread.currentThread().getName() + " 执行异步任务:" + i);
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("end executeAsync");
        }
    
        @Async("my-executor")
        @Override
        public void executeMyTask(Integer i) {
            System.out.println("---线程" + Thread.currentThread().getName() + " 执行异步任务:" + i);
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("---end executeAsync");
        }
    
    }
    

    修改启动类和创建测试类

      在启动类添加注解@EnableAsync,以支持异步操作。然后,创建一个controller:

    import com.swagger.demo.service.AsyncTaskService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author 测试线程池
     */
    @Slf4j
    @RestController
    @RequestMapping("/pool")
    public class GuavaController {
        @Autowired
        private AsyncTaskService asyncTaskService;
    
        @PostMapping("asyncTaskTest")
        public void asyncTaskTest() {
    		// 请求参数写为固定值
            asyncTaskService.executeAsyncTask(1);
            asyncTaskService.executeMyTask(200);
            System.out.println("All tasks finished.");
        }
    
    }
    

      启动Spring Boot服务后,请求如上API,在控制台可以看到如下日志:

    线程first-executor 执行异步任务:1
    All tasks finished.
    ---线程my-executor 执行异步任务:200
    end executeAsync
    ---end executeAsync
    

    从执行结果来看,我们配置的两个执行器都成功了。

    小结|@Async的使用方法

      在使用@Async注解的时候,有以下几点需要注意,

    1. 返回值:若不需要返回值,则直接设置方法返回值为void;若需要,则返回值用AsyncResult或者CompletableFuture承接。
    2. 自定义执行器,例如:@Async("theGivenExecutor")。
    3. 如何开启异步:@Async注解必须在不同类间调用,如 A类—>B类.methodC()(@Async添加在B类的方法methodC上)。若在同一个类中进行方法嵌套调用,会变同步执行,例如:A类.methodB()—>A类.@Async methodC()。
    4. @Async加到类上,表示这个类的所有方法都是异步执行,并且方法上的注解会覆盖类上的注解。慎用此种使用注解的方式!

      老铁们, 因个人能力有限,难免有瑕疵,如果发现bug或者有更好的建议,那么请在文章下方留言!

    Reference

    https://www.jianshu.com/p/832f2b162450


      读后有收获,小礼物走一走,请作者喝咖啡。
      Buy me a coffee. ☕
  • 相关阅读:
    实现activity跳转动画的若干种方式
    Go语言下载网络图片或文件
    安卓获取输入法高度与ViewTreeObserver讲解
    记录某项目中的踩坑与解决(持续更新)
    Flutter安装教程
    安卓多个RecyclerView滑动与显示问题
    java.lang.IllegalStateException: FragmentManager is already executing transactions 及 SmartTabLayout复用
    Java实现中文词频统计
    知乎视频下载(爬虫)
    SuperSpider(简书爬虫JAVA版)
  • 原文地址:https://www.cnblogs.com/east7/p/15781495.html
Copyright © 2020-2023  润新知