调用接口时,如果后台需要处理的时间过长,需要采取异步处理,先把结果返回给前台。
1、原生的
接口定义:
@RequestMapping(value="/test")publicObject test(){
MyExecutor myExecutor= newMyExecutor();try{
myExecutor.work();
}catch(Exception e) {
System.out.println("myExecutor.work()"+e.getMessage());
}
System.out.println("返回结果: "+newDate());return "成功";
}
业务执行:
importjava.util.Date;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;public classMyExecutor {private ExecutorService executor =Executors.newCachedThreadPool() ;public void work() throwsException {
executor.submit(newRunnable(){public voidrun() {try{
System.out.println("开始处理业务。。。 "+newDate());
Thread.sleep(5000);
System.out.println("业务结束。。。 "+newDate());
}catch(Exception e) {
System.out.println(e.getMessage());
}
}
});
}
}
控制台输出:
返回结果: Sat Jul 27 09:28:33 GMT+08:00 2019开始处理业务。。。 Sat Jul27 09:28:33 GMT+08:00 2019业务结束。。。 Sat Jul27 09:28:38 GMT+08:00 2019
2、在springboot 中使用
controller层
@RequestMapping(value="/async2")publicObject testAsync2(){System.out.println(1111);
userService.testAsync();
System.out.println(12);return "asdfas";
}
异步方法加注解 @Async (org.springframework.scheduling.annotation.Async;)
@Override
@Asyncpublic voidtestAsync() {
System.out.println("开始异步处理业务。。");try{
Thread.sleep(5000);
System.out.println("结束。。。。。");
}catch(InterruptedException e) {
e.printStackTrace();
}
}
启动类加注解 @EnableAsync 开启异步支持
扩展:
异步方法有返回值
@Asyncpublic FutureasyncMethodWithReturnType() {
System.out.println("Execute method asynchronously - "+Thread.currentThread().getName());try{
Thread.sleep(5000);return new AsyncResult("hello world !!!!");
}catch(InterruptedException e) {//}return null;
}
异步操作的执行器
默认情况下,Spring 使用SimpleAsyncTaskExecutor去执行这些异步方法(此执行器没有限制线程数)。此默认值可以从两个层级进行覆盖:
1. 方法级别覆盖
@Async("threadPoolTaskExecutor")public voidasyncMethodWithConfiguredExecutor() {
System.out.println("Execute method with configured executor - "+Thread.currentThread().getName());
}
2. 应用级别覆盖
配置类应该实现AsyncConfigurer接口——这意味着它拥有getAsyncExecutor()方法的实现。在这里,我们将返回整个应用程序的执行器——这现在成为运行带有@Async注释的方法的默认执行器:(异步中的线程名称-- ThreadPoolTaskExecutor-1 ,与所使用的执行相关)
importjava.util.concurrent.Executor;importorg.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;importorg.springframework.scheduling.annotation.AsyncConfigurer;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsyncpublic class SpringAsyncConfig implementsAsyncConfigurer{
@OverridepublicExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor= newThreadPoolTaskExecutor();
executor.initialize();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);returnexecutor;
}
@OverridepublicAsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return newCustomAsyncExceptionHandler();
}
}
CustomAsyncExceptionHandler 是一个自定义的异常捕捉,当方法返回值是Future的时候,异常捕获是没问题的 - Future.get()方法会抛出异常。但是,如果返回类型是Void,那么异常在当前线程就捕获不到。因此,我们需要添加额外的配置来处理异常。
通过实现AsyncUncaughtExceptionHandler接口创建一个定制的async异常处理程序。handleUncaughtException()方法在存在任何未捕获的异步异常时调用:
importjava.lang.reflect.Method;importorg.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;public class CustomAsyncExceptionHandler implementsAsyncUncaughtExceptionHandler {
@Overridepublic voidhandleUncaughtException(
Throwable throwable, Method method, Object... obj) {
System.out.println("Exception message - " +throwable.getMessage());
System.out.println("Method name - " +method.getName());for(Object param : obj) {
System.out.println("Parameter value - " +param);
}
}
}
3、spring MVC 中使用
需要加配置
相关的配置:
配置:
executor:指定一个缺省的executor给@Async使用。
例子:
配置参数:
id:当配置多个executor时,被@Async("id")指定使用;也被作为线程名的前缀。
pool-size:
core size:最小的线程数,缺省:1
max size:最大的线程数,缺省:Integer.MAX_VALUE
queue-capacity:当最小的线程数已经被占用满后,新的任务会被放进queue里面,当这个queue的capacity也被占满之后,pool里面会创建新线程处理这个任务,直到总线程数达到了max size,这时系统会拒绝这个任务并抛出TaskRejectedException异常(缺省配置的情况下,可以通过rejection-policy来决定如何处理这种情况)。缺省值为:Integer.MAX_VALUE
keep-alive:超过core size的那些线程,任务完成后,再经过这个时长(秒)会被结束掉
rejection-policy:当pool已经达到max size的时候,如何处理新任务
ABORT(缺省):抛出TaskRejectedException异常,然后不执行
DISCARD:不执行,也不抛出异常
DISCARD_OLDEST:丢弃queue中最旧的那个任务
CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
配置例子:
实例:
@Override
@Async("logExecutor") //如果不指定名字,会使用缺省的“asyncExecutor”
public void saveUserOpLog(TabUserOpLog tabUserOpLog) {
userOpLogDAO.insertTabUserOpLog(tabUserOpLog);
}
线程的优先级和类型:
优先级:NORM_PRIORITY
类型:非守护线程
用户线程(User Thread):JVM会等待所有的用户线程结束后才退出;当系统中没有用户线程了,JVM也就退出了
守护线程(Daemon Thread):一般是为其他线程提供服务的线程,比如GC垃圾回收器;JVM退出时,不会管守护线程是否存在,而是直接退出
所以,对于文件、数据库的操作,不适宜使用守护线程,不然可能会丢失数据!
Web应用停止时,Spring容器会被关闭,调用者如果是Spring bean,就会停止生成新任务。然而,线程池中已经在运行的任务,由于缺省是用户线程,所以JVM会等待它们结束后才退出。
附:Java编程方式的配置方法:
@Configuration
@EnableAsyncpublic classSpringConfig {/**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;private String ThreadNamePrefix = "MyLogExecutor-";
@BeanpublicExecutor logExecutor() {
ThreadPoolTaskExecutor executor= newThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix(ThreadNamePrefix);//rejection-policy:当pool已经达到max size的时候,如何处理新任务//CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();returnexecutor;
}
}
SimpleAsyncTaskExecutor