Spring @Async注解实现异步方法

一、使用方式

实现异步方法比较简单,只需下边两步

  1. 在启动类或者配置类上添加 @EnableAsync 注解;
  2. 在方法上加 @Async 注解。

示例代码

@Service
@EnableAsync
public class TestAsyncServiceImpl {

    /**
     * 没有返回值的异步方法
     */
    @Async
    public void testAsync1() {
        //  do something
    }

    /**
     * 有返回值的异步方法
     * @return
     */
    @Async
    public Future<String> testAsync2(){
        // do something
        Future<String> future = new AsyncResult<String>("success");
        return future;
    }
}

注意事项

  • 同一个类中的同步方法调用本类中的@Async 异步方法,异步方法不生效;
  • @Async 异步方法的线程池,默认使用SimpleAsyncTaskExecutor,这个不是真的线程池,它不会重复使用线程,每次调用都会创建一个新的线程,并发大的时候会产生严重的性能问题。
二、自定义线程池

@Async 异步方法的默认线程池是有风险问题的,我们可以指定自定义线程池。
下边给出两种自定义线程池的方式,我们在用的时候二选一即可。

  1. @Bean注解方式

    @Configuration
    public class TestAsyncThreadPool {
    
        @Bean("testAsyncExecutor")
        public Executor testExecutor(){
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            //核心池大小:线程池维护线程的最少数量
            executor.setCorePoolSize(5);
            //最大线程数
            executor.setMaxPoolSize(100);
            //队列大小
            executor.setQueueCapacity(1000);
            //线程空闲时间
            executor.setKeepAliveSeconds(60);
            //线程名称前缀
            executor.setThreadNamePrefix("test-asyn-");
            //拒绝策略:当最大线程数已经达到maxPoolSize的时候,如何处理新任务
            //  ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
            //  ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 
            //  ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,执行后面的任务
            //  ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
            //线程池注册优雅停机:当要停止应用时,等待所有线程执行完再停止
            executor.setWaitForTasksToCompleteOnShutdown(true);
            //设置等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
            executor.setAwaitTerminationSeconds(120);
            return executor;
        }
    }
    
  2. 传统xml方式

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
    	<bean id="testAsyncExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    		<!-- 线程池维护线程的最少数量 -->
    		<property name="corePoolSize" value="5" />
    		<!-- 线程池维护线程的最大数量 -->
    		<property name="maxPoolSize" value="100" />
    		<!-- 允许的空闲时间 -->
    		<property name="keepAliveSeconds" value="1000" />
    		<!-- 缓存队列 -->
    		<property name="queueCapacity" value="60" />
    		<!-- 线程名称前缀 -->
    		<property name="threadNamePrefix" value="test-asyn-" />
    		<!-- 对拒绝task的处理策略 -->
    		<property name="rejectedExecutionHandler">
    			<bean class="java.util.concurrent.ThreadPoolExecutor$AbortPolicy" />
    		</property>
    		<!-- 线程池注册优雅停机:当要停止应用时,等待所有线程执行完再停止 -->
    	    <property name="waitForTasksToCompleteOnShutdown" value="true"/>
    	    <!-- 设置等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住 -->
    	    <property name="awaitTerminationSeconds" value="120"/>
    	</bean>
    
    </beans>
    
    

    xml文件可以通过dispatcherservlet加载,也可以通过配置类引入:

    @Configuration
    @ImportResource(value = { "classpath:spring/spring-threadpool.xml" })
    public class TestAsyncThreadPool {
    }
    
三、自定义线程池的使用

使用方式比较简单,直接将之前的@Async改为@Async("testAsyncExecutor")

@Service
@EnableAsync
public class TestAsyncServiceImpl {

    /**
     * 没有返回值的异步方法
     */
    @Async("testAsyncExecutor")
    public void testAsync1() {
        //  do something
    }

    /**
     * 有返回值的异步方法
     * @return
     */
    @Async("testAsyncExecutor")
    public Future<String> testAsync2(){
        // do something
        Future<String> future = new AsyncResult<String>("success");
        return future;
    }
}
四、线程池execute(Runable)方法执行过程

处理优先级:corePool > queue > maxPool > rejectedExecutionHandler

  1. 如果线程池中的线程数 < corePoolSize,即使有空闲线程,新请求也会新建线程处理;
  2. 如果线程池中的线程数 = corePoolSize,有空闲线程,新请求会使用空闲的线程;
  3. 如果线程池中的线程数 = corePoolSize,没有空闲线程,队列中数据 < queueCapacity,新请求会被放到队列中;
  4. 如果队列中数据 = queueCapacity,新请求会继续新建线程处理;
  5. 如果线程池中的线程数 = maxPoolSize,新请求会通过配置的拒绝策略处理。
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓呆同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值