重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章主要为大家展示了“@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怎么用”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!