@Async
默认异步配置使用的是SimpleAsyncTaskExecutor
,该线程池默认来一个任务创建一个线程,在大量的请求的时候,这时就会不断创建大量线程,极有可能压爆服务器内存
@Async
的时候一定要设置线程数,以防万一OOM
异步调用,类似我们多年前的ajax调用,局部刷新,整体不变,当然,在java的后台的异步调用,类似于自己实现一个多线程的程序,任务开启一个线程后由它最去执行,我们其实是不能干预太多的。。
在实际的开发中,如果某一个方法需要异步去执行,那么我们可以在它前面加上注解。
@Async
1、 @Async
介绍
在Spring中,基于@Async
标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。分为不带参数的异步调用;带参数的异步调用;调用返回Future的异步线程
2、@Async
调用中的事务处理机制
在@Async
标注的方法,同时也适用了@Transactional
进行了标注;
在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。
那该如何给这些操作添加事务管理呢?
可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional
. @Transactional需要用在调用方法上,用在异步方法上不会生效。
例如:
方法A,同时使用了@Async/@Transactional
来标注,但是无法产生事务控制的目的。
方法B,使用了@Async
来标注, B中调用了C、D,C/D分别使用@Transactional
做了标注,则可实现事务控制的目的。
3、配合使用@EnableAsync
在启动类
或者Controller
类加上@EnableAsync
注解
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@EnableAsync
@RestController
public class HelloController {
@Autowired
TestAsyncService testAsyncService;
}
@EnableAsync
注解的意思是可以异步执行,就是开启多线程的意思。可以标注在方法、类上。@Async
所修饰的函数不要定义为static类型,这样异步调用不会生效
4、举例:
比如需要调用一个发送短信的任务,实际短信是渠道方去发的,那么我们在把请求提交过去基本就结束了,这个时候就可以做一个异步的调用来实现。
先使用@EnableAsync
来开启异步的支持,配置一个线程池:
Spring 4
中,对异步方法可以做一些配置,将配置类实现AsyncConfigurer
接口后,可以实现自定义线程池的功能,和统一处理异步方法的异常。如果不限制并发数,可能会造成系统压力。
AsyncConfigurer
接口中的方法Executor getAsyncExecutor()
实现自定义线程池。控制并发数。
AsyncConfigurer
接口中的方法public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler()
用于处理异步方法的异常。
AsyncUncaughtExceptionHandler
接口,只有一个方法:void handleUncaughtException(Throwable ex, Method method, Object… params);
因此,
AsyncUncaughtExceptionHandler
接口可以认为是一个函数式接口,可以用拉姆达表达式实现该接口。当然不处理也是可以的,没用直接返回null
@Configuration
@EnableAsync
public class ThreadPoolConfig implements AsyncConfigurer {
/**
* ThreadFactory 为线程池创建的线程命名
*/
private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("example-pool-%d").build();
/**
* 获取异步线程池执行对象
*/
@Override
public Executor getAsyncExecutor() {
// 使用Spring内置线程池任务对象
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置线程池参数
executor.initialize();
// 获取到服务器的cpu内核
int i = Runtime.getRuntime().availableProcessors();
// 核心池大小
executor.setCorePoolSize(5);
// 最大线程数
executor.setMaxPoolSize(100);
// 队列容量
executor.setQueueCapacity(1000);
// 线程空闲时间(秒)
executor.setKeepAliveSeconds(1000);
// 线程前缀名称
executor.setThreadNamePrefix("task-async-");
// 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务 CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
// return (throwable, method, objects) -> System.out.println(
// "-- exception handler -- " + throwable + "-- method -- " + method + "-- objects -- " + objects);
}
/**
* 自定义线程池
*/
@Bean(name = "asyncTaskExecutor")
public Executor asyncTaskExecutor() {
//获取CPU 核心数
int nThreads = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
nThreads,
2 * nThreads + 5,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
threadFactory,
new ThreadPoolExecutor.CallerRunsPolicy());
// 先行创建符合corePoolSize参数值的线程数
threadPoolExecutor.prestartAllCoreThreads();
return threadPoolExecutor;
}
}
如果我们项目中需要多个线程池,则可以参考
asyncTaskExecutor()
方法实例出自定义的线程池asyncTaskExecutor
,在使用的时候使用@Async("asyncTaskExecutor")
来实现使用asyncTaskExecutor
的线程池实现异步操作。
@RestController
@RequestMapping("async")
public class AsyncController {
@Autowired
private AsyncServiceImpl asyncService;
@RequestMapping("task")
public String task() throws Exception {
long currentTimeMillis = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "主线程请求异步执行task1()");
asyncService.task1();
System.out.println(Thread.currentThread().getName() + "主线程请求异步执行task1()结束");
System.out.println(Thread.currentThread().getName() + "主线程请求异步执行task2()");
asyncService.task2();