SpringBoot中使用线程池
1.配置线程池使用springboot配置线程池,采用@EnableAsync和@Configuration注解,配置代码如下@EnableAsync@Configurationpublic class SpringThreadPoolConfig {@Value("${mythreadpool.maxPoolSize}")private Integer maxPoolSize;@Value("$
·
1.配置线程池
使用springboot配置线程池,采用@EnableAsync和@Configuration注解,配置代码如下
@EnableAsync
@Configuration
public class SpringThreadPoolConfig {
@Value("${mythreadpool.maxPoolSize}")
private Integer maxPoolSize;
@Value("${mythreadpool.corePoolSize}")
private Integer corePoolSize;
@Value("${mythreadpool.queueCapacity}")
private Integer queueCapacity;
@Value("${mythreadpool.keepAliveSeconds}")
private Integer keepAliveSeconds;
@Value("${mythreadpool.threadNamePrefix}")
private String threadNamePrefix;
@Value("${mythreadpool.waitForTasksToCompleteOnShutdown}")
private Boolean waitForTasksToCompleteOnShutdown;
@Bean("taskExecutor")
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数等于系统核数--8核
int availableProcessors = Runtime.getRuntime().availableProcessors();
executor.setCorePoolSize(availableProcessors);
// 设置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(keepAliveSeconds);
// 线程满了之后由调用者所在的线程来执行
// 拒绝策略:CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 设置默认线程名称
executor.setThreadNamePrefix(threadNamePrefix);
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown);
//执行初始化
executor.initialize();
return executor;
}
}
线程池的参数配置在application-dev.yml中,具体如下
mythreadpool:
maxPoolSize: 20
corePoolSize: 8
queueCapacity: 2048
keepAliveSeconds: 60
threadNamePrefix: springThreadPool
waitForTasksToCompleteOnShutdown: true
2.创建异步调用接口以及实现类
这里主要是使用到@Async("XXX")这个注解来声明使用XXX线程池异步执行该方法,代码如下
public interface AsyncService {
public String doMethod(String message);
}
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
@Async("taskExecutor")
@Override
public String doMethod(String message) {
String fileName ="C://EasyExcel.xlsx";
EasyExcel.read(fileName, DemoData.class, new DemoDatalistener()).sheet().doRead();
return message;
}
}
@RestController
public class AsyncController {
@Autowired
AsyncService asyncService;
@GetMapping("/sync/testMethod")
public String testMethod() {
Instant beginTime = Instant.now();
asyncService.doMethod("message");
Instant endTime = Instant.now();
System.out.println("时间差(毫秒):"+ Duration.between(beginTime, endTime).toMillis());
return "success";
}
}
3.业务逻辑代码
为了展示使用线程的好处,直观的对比线程池使用前后方法耗时时间,加入了一些业务的处理。业务假设为使用easyexcel读取文件,其中相关代码如下
@Data
public class DemoData {
@ExcelProperty("商品名称")
private String name;
@ExcelProperty("日期")
private Date date;
@ExcelProperty("金额")
private Double doubleData;
}
public class DemoDatalistener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDatalistener.class);
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoSeviceImpl demoSeviceImpl;
public DemoDatalistener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoSeviceImpl = new DemoSeviceImpl();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoSeviceImpl
*/
public DemoDatalistener(DemoSeviceImpl demoSeviceImpl) {
this.demoSeviceImpl = demoSeviceImpl;
}
@Override
public void invoke(DemoData demoData, AnalysisContext analysisContext) {
//System.out.println(JSON.toJSONString(demoData));
list.add(demoData);
//System.out.println(list);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();//自定义的业务
// 存储完成清理 list
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
demoSeviceImpl.save(list);
}
}
public interface DemoService {
public void save(List<DemoData> list);
}
@Service
public class DemoSeviceImpl implements DemoService {
@Override
public void save(List<DemoData> list) {
}
}
4.执行结果
在第三步中,对@Async注解进行注释前后对比
//@Async("taskExecutor")
在浏览器访问开5个窗口访问,其前后结果如下
注释前:
时间差(毫秒):3
时间差(毫秒):1
时间差(毫秒):0
时间差(毫秒):1
时间差(毫秒):1
2022-04-20 15:16:12.490 INFO 23932 --- [ringThreadPool1] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
2022-04-20 15:16:51.197 INFO 23932 --- [ringThreadPool2] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
2022-04-20 15:16:52.980 INFO 23932 --- [ringThreadPool3] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
2022-04-20 15:16:54.640 INFO 23932 --- [ringThreadPool4] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
2022-04-20 15:16:55.602 INFO 23932 --- [ringThreadPool5] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
注释后:
2022-04-20 16:04:02.422 INFO 19832 --- [nio-8082-exec-1] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
时间差(毫秒):46973
2022-04-20 16:04:39.058 INFO 19832 --- [nio-8082-exec-2] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
时间差(毫秒):62450
2022-04-20 16:04:40.914 INFO 19832 --- [nio-8082-exec-3] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
时间差(毫秒):62802
2022-04-20 16:04:42.504 INFO 19832 --- [nio-8082-exec-4] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
时间差(毫秒):62586
2022-04-20 16:04:43.373 INFO 19832 --- [nio-8082-exec-5] c.r.demo.listener.DemoDatalistener : 所有数据解析完成!
时间差(毫秒):61924
更多推荐
已为社区贡献1条内容
所有评论(0)