@async 无效的原因总结如下
- 在@SpringBootApplication启动类 添加注解@EnableAsync
- 异步方法使用注解@Async ,返回值为void或者Future
- 切记一点 ,异步方法和调用方法一定要**** 写在不同的类中 ****,如果写在一个类中,
是没有效果的
实现异步新线程调用,Springboot简单配置:
1、在主类中添加@EnableAsync注解:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2、创建一个AsyncTask类,在里面添加两个用@Async注解的task:
@Component
public class AsyncTask {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Async
public Future<String> doTask1() throws InterruptedException{
logger.info("Task1 started.");
long start = System.currentTimeMillis();
Thread.sleep(5000);
long end = System.currentTimeMillis();
logger.info("Task1 finished, time elapsed: {} ms.", end-start);
return new AsyncResult<>("Task1 accomplished!");
}
@Async
public Future<String> doTask2() throws InterruptedException{
logger.info("Task2 started.");
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
logger.info("Task2 finished, time elapsed: {} ms.", end-start);
return new AsyncResult<>("Task2 accomplished!");
}
}
测试代码:注测试Junit版本4.12以上
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BasicUtClass {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected final ObjectMapper objectMapper = new ObjectMapper();
}
public class TaskTests extends BasicUtClass {
@Autowired
private AsyncTask asyncTask;
@Test
public void AsyncTaskTest() throws InterruptedException, ExecutionException {
Future<String> task1 = asyncTask.doTask1();
Future<String> task2 = asyncTask.doTask2();
while(true) {
if(task1.isDone() && task2.isDone()) {
logger.info("Task1 result: {}", task1.get());
logger.info("Task2 result: {}", task2.get());
break;
}
Thread.sleep(1000);
}
logger.info("All tasks finished.");
}
}
运行结果:
2018-09-07 22:38:11.335 INFO 13908 --- [ main] .s.a.AnnotationAsyncExecutionInterceptor : More than one TaskExecutor bean found within the context, and none is named 'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly as an alias) in order to use it for async processing: [mySimpleAsync, myAsync]
2018-09-07 22:38:11.369 INFO 13908 --- [cTaskExecutor-1] com.work.spring.thread.demo3.AsyncTask : Task1 started.
2018-09-07 22:38:11.369 INFO 13908 --- [cTaskExecutor-2] com.work.spring.thread.demo3.AsyncTask : Task2 started.
2018-09-07 22:38:14.369 INFO 13908 --- [cTaskExecutor-2] com.work.spring.thread.demo3.AsyncTask : Task2 finished, time elapsed: 3000 ms.
2018-09-07 22:38:16.369 INFO 13908 --- [cTaskExecutor-1] com.work.spring.thread.demo3.AsyncTask : Task1 finished, time elapsed: 5000 ms.
2018-09-07 22:38:17.351 INFO 13908 --- [ main] com.work.spring.thread.TaskTests : Task1 result: Task1 accomplished!
2018-09-07 22:38:17.351 INFO 13908 --- [ main] com.work.spring.thread.TaskTests : Task2 result: Task2 accomplished!
2018-09-07 22:38:17.351 INFO 13908 --- [ main] com.work.spring.thread.TaskTests : All tasks finished.
可以看到,没有自定义的Executor,所以使用缺省的TaskExecutor 。
自定义的Executor,可以按照如下几步来:
1、新建一个Executor配置类,顺便把@EnableAsync注解搬到这里来:
@Configuration
@EnableAsync
public class ExecutorConfig {
/** Set the ThreadPoolExecutor's core pool size. */
private int corePoolSize = 10;
/** Set the ThreadPoolExecutor's maximum pool size. */
private int maxPoolSize = 200;
/** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */
private int queueCapacity = 10;
@Bean
public Executor mySimpleAsync() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("MySimpleExecutor-");
executor.initialize();
return executor;
}
@Bean
public Executor myAsync() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("MyExecutor-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
这里定义了两个不同的Executor,第二个重新设置了pool已经达到max size时候的处理方法;同时指定了线程名字的前缀。
2、自定义Executor的使用:
@Component
public class AsyncTaskDemo4 {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Async("mySimpleAsync")
public Future<String> doTask1() throws InterruptedException{
logger.info("Task1 started.");
long start = System.currentTimeMillis();
Thread.sleep(5000);
long end = System.currentTimeMillis();
logger.info("Task1 finished, time elapsed: {} ms.", end-start);
return new AsyncResult<>("Task1 accomplished!");
}
@Async("myAsync")
public Future<String> doTask2() throws InterruptedException{
logger.info("Task2 started.");
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
logger.info("Task2 finished, time elapsed: {} ms.", end-start);
return new AsyncResult<>("Task2 accomplished!");
}
}
测试方法同上
测试ji结果:
2018-09-07 22:41:44.429 INFO 13640 --- [ main] com.work.spring.thread.TaskTests2 : Started TaskTests2 in 4.179 seconds (JVM running for 5.853)
2018-09-07 22:41:44.525 INFO 13640 --- [ MyExecutor-1] c.w.spring.thread.demo4.AsyncTaskDemo4 : Task2 started.
2018-09-07 22:41:44.525 INFO 13640 --- [impleExecutor-1] c.w.spring.thread.demo4.AsyncTaskDemo4 : Task1 started.
2018-09-07 22:41:47.526 INFO 13640 --- [ MyExecutor-1] c.w.spring.thread.demo4.AsyncTaskDemo4 : Task2 finished, time elapsed: 3001 ms.
2018-09-07 22:41:49.526 INFO 13640 --- [impleExecutor-1] c.w.spring.thread.demo4.AsyncTaskDemo4 : Task1 finished, time elapsed: 5001 ms.
2018-09-07 22:41:50.524 INFO 13640 --- [ main] com.work.spring.thread.TaskTests2 : Task1 result: Task1 accomplished!
2018-09-07 22:41:50.524 INFO 13640 --- [ main] com.work.spring.thread.TaskTests2 : Task2 result: Task2 accomplished!
2018-09-07 22:41:50.524 INFO 13640 --- [ main] com.work.spring.thread.TaskTests2 : All tasks finished.
2018-09-07 22:41:50.528 INFO 13640 --- [ Thread-3] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@747f281: startup date [Fri Sep 07 22:41:40 CST 2018]; root of context hierarchy
2018-09-07 22:41:50.532 INFO 13640 --- [ Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'myAsync'
2018-09-07 22:41:50.532 INFO 13640 --- [ Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'mySimpleAsync'
可见,线程名字的前缀变了,两个task使用了不同的线程池了。
扩展ThreadPoolTaskExecutor
虽然我们已经用上了线程池,但是还不清楚线程池当时的情况,有多少线程在执行,多少在队列中等待呢?这里我创建了一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来,代码如下:
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);
private void showThreadPoolInfo(String prefix){
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if(null==threadPoolExecutor){
return;
}
logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2. do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}
如上所示,showThreadPoolInfo方法中将任务总数、已完成数、活跃线程数,队列大小都打印出来了,然后Override了父类的execute、submit等方法,在里面调用showThreadPoolInfo方法,这样每次有任务被提交到线程池的时候,都会将当前线程池的基本情况打印到日志中;
修改ExecutorConfig.java的asyncServiceExecutor方法,将ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改为ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor(),如下所示:
//使用VisiableThreadPoolTaskExecutor
ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
线程任务总数、已完成数、活跃线程数,队列大小等情况一目了然。