温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

@Async怎么用

发布时间:2021-07-07 18:36:57 来源:亿速云 阅读:208 作者:小新 栏目:编程语言

这篇文章主要为大家展示了“@Async怎么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“@Async怎么用”这篇文章吧。

使用@Async注解的方法会新开一个线程去执行该方法,一些比较耗时的操作的操作使用该注解,交由Spring去管理线程的创建与销毁。

接下来阐述一下自己的使用心得:
一、启用@Async,个人在Springboot的启动类加上以下注解

@EnableAsync

二、在需要进行异步操作的方法上加上@Async注解,注解不起作用的主要原因是被调用方法和调用方法处理同一个类中。 失效的代码

class TestService {
    void a() { 
      this.b();
    }
	
    @Async
    void b(){}
}

正常的代码

class TestService {
    void a(){ 
       BService.b();
    }
}

class BService() {
    @Async
    void b(){}
}

这个时候我们可能会想到AopContext.currentProxy(),这样就可以在同一个方法里面调用了

class TestService {
	
   TestService getSelf() {
      return (TestService)AopContext.currentProxy();
   }
	
    void a() { 
      getSelf().b();
    }
	
    @Async
    void b(){}
}

在这里可能会抛出以下的异常,具体的原因及解决思路请参考这篇文章

java.lang.IllegalStateException: Cannot find current proxy:
Set 'exposeProxy' property on Advised to 'true' to make it available.

三、接下来了解一下@Async使用的线程池
@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor(参考TaskExecutionAutoConfiguration),其中默认核心线程数为8,默认最大队列和默认最大线程数都是Integer.MAX_VALUE。创建新线程的条件是队列填满时,而这样的配置队列永远不会填满,如果有@Async注解标注的方法长期占用线程(比如HTTP长连接等待获取结果),在核心8个线程数占用满了之后,新的调用就会进入队列, 外部表现为没有执行。
我们可以自定义一个线程池,线程数的设定需要考虑一下要执行的任务是IO密集型任务,还是CPU密集型任务。对于CPU密集型任务,如CPU核数+1;对于IO密集型任务,由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如CPU核数*2。
接下来给出一个IO密集型任务的线程池配置代码

@Configuration
public class ThreadPoolConfig {

    /**
     * 核心线程数
     */
    private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;

    /**
     * 最大线程数
     */
    private static final int MAX_POOL_SIZE = CORE_POOL_SIZE * 4 < 256 ? 256 : CORE_POOL_SIZE * 4;

    /**
     * 允许线程空闲时间(单位为秒)
     */
    private static final int KEEP_ALIVE_TIME = 10;

    /**
     * 缓冲队列数
     */
    private static final int QUEUE_CAPACITY = 200;

    /**
     * 线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁
     */
    private static final int AWAIT_TERMINATION = 60;

    /**
     * 用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
     */
    private static final Boolean WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN = true;

    /**
     * 线程池名前缀
     */
    private static final String THREAD_NAME_PREFIX = "Spider-ThreadPool-";


    @Bean("spiderTaskExecutor")
    public ThreadPoolTaskExecutor spiderTaskExecutor () {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
        taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
        taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        taskExecutor.setWaitForTasksToCompleteOnShutdown(WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN);
        taskExecutor.setAwaitTerminationSeconds(AWAIT_TERMINATION);
        /**
         * 拒绝策略 => 当pool已经达到max size的时候,如何处理新任务
         * CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
         * AbortPolicy:直接抛出异常,这是默认策略;
         * DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
         * DiscardPolicy:直接丢弃任务;
         */
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }

}

线程池的使用

@Async("spiderTaskExecutor")

最后补充一些知识,要合理的控制线程数(比如采集订单信息的同时要采集订单详情和文章信息,订单详情和文章信息可以合并在一个线程中处理),不要滥用。需要考虑什么时候使用MQ,什么时候开启线程异步处理。推荐一个分析jstack文件的工具,IBM Thread and Monitor Dump Analyzer for Java,分析一下正在运行、发生死锁、等待、阻塞的线程。

以上是“@Async怎么用”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI