1. 异步场景分析
在开发系统的过程中,通常会考虑到系统的性能问题,提升系统性能的一个重要思想
就是“串行”改“并行”。说起“并行”自然离不开“异步”,今天我们就来聊聊如何使用
Spring的 @Async
的异步注解。
2. Spring 业务的异步实现
2.1 启动异步配置
在基于注解方式的配置中,借助@EnableAsync注解进行异步启动声明,Spring
Boot版的项目中,将@EnableAsync
注解应用到启动类上, 代码示例如下:
@EnableAsync //spring容器启动时会创建线程池
@SpringBootApplication
public class Application {
public static void main(String[] args ) {
SpringApplication. run (Application. class , args );
}
}
2.2 Spring中@Async注解应用
在需要异步执行的业务方法上,使用@Async
方法进行异步声明。
@Async
@Transactional (propagation = Propagation. REQUIRES_NEW )
@Override
public void saveObject(SysLog entity ) {
System. out .println("SysLogServiceImpl.save:" + Thread. currentThread().getName());
sysLogDao .insertObject( entity );
// try {Thread. sleep (5000);} catch (Exception e ) {}
}
假如需要获取业务层异步方法的执行结果,可参考如下代码设计进行实现:
@Transactional (propagation = Propagation. REQUIRES_NEW )
@Async
@Override
public Future<Integer> saveObject(SysLog entity ) {
System. out .println("SysLogServiceImpl.save:"+ Thread. currentThread ().getName());
int rows = sysLogDao .insertObject( entity );
// try {Thread. sleep (5000);} catch (Exception e ) {}
return new AsyncResult<Integer>( rows );
}
其中,AsyncResult对象可以对异步方法的执行结果进行封装,假如外界需要异步方
法结果时,可以通过Future对象的get方法获取结果。
当我们需要自己对spring框架提供的线程池进行一些简易配置,可以参考如下代码:
spring:
task:
execution:
pool:
queue-capacity: 128
core-size: 5
max-size: 128
keep-alive: 60000
thread-name-prefix: db-service-task-
对于spring框架中线程池配置参数的涵义,可以参考ThreadPoolExecutor
对象中的
解释。
说明:对于@Async
注解默认会基于ThreadPoolTaskExecutor
对象获取工作线程,然
后调用由@Async
描述的方法,让方法运行于一个工作线程,以实现异步操作。但是假如系
统中的默认拒绝处理策略,任务执行过程的异常处理不能满足我们自身业务需求的话,我可
以对异步线程池进行自定义.(SpringBoot中默认的异步配置可以参考自动配置对象
TaskExecutionAutoConfiguration
).
3. Spring 自定义异步池的实现
为了让Spring中的异步池更好的服务于我们的业务,同时也尽量避免OOM,可以自定义
线程池优化设计如下:关键代码如下:
package com.cy.pj.common.config
@Slf4j
@Setter
@Configuration
@ConfigurationProperties( "async-thread-pool" )
public class SpringAsyncConfig implements AsyncConfigurer{
/**核心线程数*/
private int corePoolSize =20;
/**最大线程数*/
private int maximumPoolSize=1000;
/**线程空闲时间*/
private int keepAliveTime =30;
/**阻塞队列容量*/
private int queueCapacity =200;
/**构建线程工厂*/
private ThreadFactory threadFactory = new ThreadFactory() {
//CAS算法
private AtomicInteger at = new AtomicInteger(1000);
@Override
public Thread newThread(Runnable r ) {
return new Thread( r , "db-async-thread-" + at .getAndIncrement());
}
};
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor .setCorePoolSize( corePoolSize );
executor .setMaxPoolSize( maximumPoolSize );
executor .setKeepAliveSeconds( keepAliveTime );
executor .setQueueCapacity( queueCapacity );
executor .setRejectedExecutionHandler((Runnable r ,
ThreadPoolExecutor exe ) -> {
log .warn( "当前任务线程池队列已满." );
});
executor .initialize();
return executor ;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex ,
Method method , Object... params ) { log .error( "线程池执行任务发生未知异常." , ex );
}
};
}
}
其中:@ConfigurationProperties(“async-thread-pool”)的含义是读取
application.yml配置文件中以"async-thread-pool"名为前缀的配置信息,并通过所描
述类的set方法赋值给对应的属性,在application.yml中连接器池的关键配置如下:
async-thread-pool:
corePoolSize: 20
maxPoolSize: 1000
keepAliveSeconds: 30
queueCapacity: 1000
后续在业务类中,假如我们使用@Async注解描述业务方法,默认会使用
ThreadPoolTaskExecutor
池对象中的线程执行异步任务。