不扯淡了,直接开干,注意点在最后一定一定要看。
在Springboot中对其进行了简化处理,只需要配置一个类型为java.util.concurrent.TaskExecutor或其子类的bean,并在配置类或直接在程序入口类上声明注解@EnableAsync
。
调用也简单,在由Spring管理的对象的方法上标注注解@Async
,显式调用即可生效。
以下是一个带有返回值的异步线程任务示例:
配置类:
/** * @Description: 配置类实现AsyncConfigurer接口,并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor, * 这样我们就获得一个基于线程池TaskExecutor * 利用@EnableAsync注解开启异步任务支持 * @ClassName: MultiThreadingConfig * @Author: xiaolege */ @Configuration @ComponentScan("com.cn.geostar.api.*") @EnableAsync public class MultiThreadingConfig implements AsyncConfigurer{ @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); //最小线程数 taskExecutor.setCorePoolSize(5); //最大线程数 taskExecutor.setMaxPoolSize(10); //等待队列 taskExecutor.setQueueCapacity(50); taskExecutor.setKeepAliveSeconds(600); //设置拒绝策略 taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //等待所有任务结束后再关闭线程池 // taskExecutor.setWaitForTasksToCompleteOnShutdown(true); taskExecutor.initialize(); return taskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler(); } }
service:
/** * @author xiaolege */ @Service @Slf4j public class HarvestMultiThreadingService { /** * 表元数据信息采集 * * @Description:通过@Async注解表明该方法是一个异步方法, * 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor */ @Async public Future<Boolean> executeAysncTableHarvest(BasicDataSource dataSource, String tableName){ try{ MetaLoader metaLoader = new MetaLoaderImpl(dataSource); Table tmd = metaLoader.getTable(tableName, SchemaInfoLevel.min()); log.info(tmd.toString()); } catch (Exception e){ e.printStackTrace(); return new AsyncResult<>(false); } //消息汇总 return new AsyncResult<>(true); } }
controller:
/** * @author xiaolege */ @RestController @Slf4j public class TableController { @Autowired private HarvestMultiThreadingService harvestMultiThreadingService; @PostMapping("getTableMeta") public GeoResponse getTableMeta(String dataSourceId) throws ExecutionException, InterruptedException { // GeoResponse geoResponse = harvestProvider.getDataSourceInfo(dataSourceId); String url = "jdbc:mysql://127.0.0.1:3306/ceshi?user=root&password=123456&serverTimezone=GMT%2b8"; String tableName = "test"; BasicDataSource dataSource = new BasicDataSource(); dataSource.setUrl(url); dataSource.setMaxActive(20); dataSource.setInitialSize(5); dataSource.setMinIdle(5); dataSource.setMaxWait(60000); int length = 50; List<Future<Boolean>> list = new ArrayList<>(50); for (int i = 0; i < length; i++) { try { Future<Boolean> future = harvestMultiThreadingService.executeAysncTableHarvest(dataSource,tableName); list.add(future); } catch (Exception e) { e.printStackTrace(); } } for (Future f:list ) { log.info(f.get().toString()); } return GeoResponse.defaultSuccess(); } }
注意:
在java中提供了Future泛型接口,用来接收任务执行结果,springboot也提供了此类支持,使用实现了ListenableFuture接口的类如AsyncResult来作为返回值的载体。如上例所示。
如果不要返回值只需要把service里面的方法改为void返回即可。
有返回值的异步任务一定一定要注意
一定要批量读取结果, 否则不能达到异步的效果!!
1、异步方法和调用类不要在同一个类中
2、注解扫描时,要注意过滤,避免重复实例化,因为存在覆盖问题,@Async就失效了