对于spring异步注解@Async的使用:
对于异步方法调用,从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。
我们尝试一次异步操作。
定义接口,我们采用有回调的future。
1 public interface AsyncService { 2 3 //涉及一个future的通道,以及一个计数使用的latch 4 Future<Integer> getFollowed(Integer times, CountDownLatch latch); 5 6 Future<Integer> getAtention(Integer times, CountDownLatch latch); 7 8 }
进行方法实现
其中一个我使用了默认线程池,另外一个指定自己的线程池稍后介绍区别
@Service public class AsyncServiceImpl implements AsyncService { @Override @Asyncpublic Future<Integer> getFollowed(Integer times, CountDownLatch latch) { Integer count = 0; //负责相关使用逻辑 for (int i = 0; i < times; i++) { count += i; }
latch.countDown(); return new AsyncResult<>(new Integer(count)); } @Override @Async("asyncExecutor") public Future<Integer> getAtention(Integer times, CountDownLatch latch) { Integer count = 0; //负责相关使用逻辑 for (int i = 0; i < times; i++) { count += i; } latch.countDown(); return new AsyncResult<>(new Integer(count)); }
在外部设置好计数器返回现有结果
long s = System.currentTimeMillis(); //计数 CountDownLatch latch = new CountDownLatch(2); Future<Integer> atention = asyncService.getAtention(times,latch); Future<Integer> followed = asyncService.getFollowed(times, latch); long e = System.currentTimeMillis(); System.out.println("asyncService spend: " + (e - s));
定义自己异步线程池
@Configuration public class AsyncThreadingConfig implements AsyncConfigurer { @Override @Bean(name = "asyncExecutor") public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); //最小线程数 taskExecutor.setCorePoolSize(5); //最大线程数 taskExecutor.setMaxPoolSize(10); //等待队列 taskExecutor.setQueueCapacity(25); // 允许线程空闲时间(单位:默认为秒) taskExecutor.setKeepAliveSeconds(30); // 线程池名前缀 taskExecutor.setThreadNamePrefix("asyncExecutor-"); //设置拒绝策略 taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //等待所有任务结束后再关闭线程池 taskExecutor.setWaitForTasksToCompleteOnShutdown(true); // 线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住 taskExecutor.setAwaitTerminationSeconds(60); taskExecutor.initialize(); return taskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler(); } }
基本上就是异步实现的过程。
首先我们Spring 已经实现的线程池
1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,默认每次调用都会创建一个新的线程。
2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方。
3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。
5. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装。
Spring应用默认的线程池,指在@Async注解在使用时,不指定线程池的名称。查看源码,@Async的默认线程池为SimpleAsyncTaskExecutor。
几种线程池会出现的问题:
在线程池应用中,参考阿里巴巴java开发规范:线程池不允许使用Executors去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确线程池的运行规则,规避资源耗尽的风险。Executors各个方法的弊端:
newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
newCachedThreadPool和newScheduledThreadPool:要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
@Async默认异步配置使用的是SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误。针对线程创建问题,SimpleAsyncTaskExecutor提供了限流机制,通过concurrencyLimit属性来控制开关,当concurrencyLimit>=0时开启限流机制,默认关闭限流机制即concurrencyLimit=-1,当关闭情况下,会不断创建新的线程来处理任务。基于默认配置,SimpleAsyncTaskExecutor并不是严格意义的线程池,达不到线程复用的功能。
@Async应用自定义线程池(第四步)
自定义线程池,可对系统中线程池更加细粒度的控制,方便调整线程池大小配置,线程执行异常控制和处理。在设置系统自定义线程池代替默认线程池时,虽可通过多种模式设置,但替换默认线程池最终产生的线程池有且只能设置一个(不能设置多个类继承AsyncConfigurer)。自定义线程池有如下模式:
通过查看Spring源码关于@Async的默认调用规则,会优先查询源码中实现AsyncConfigurer这个接口的类,实现这个接口的类为AsyncConfigurerSupport。但默认配置的线程池和异步处理方法均为空,所以,无论是继承或者重新实现接口,都需指定一个线程池。且重新实现 public Executor getAsyncExecutor()方法。
Async可能会存在失效
没有过去到代理类,本类调用时,直接自己内部调用,没有走代理类
1.没有在@SpringBootApplication启动类当中添加注解@EnableAsync注解。
2.异步方法使用注解@Async的返回值只能为void或者Future。
3.没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。
原文:https://www.cnblogs.com/badfisher/p/14805134.html