使用线程池
工作中如果需要开启多线程处理任务,可使用线程池来实现,在springboot中,可预先配置一个线程池使用,放到上下文中,也可以新创建一个,然后设置参数。
如下面所示,其中核心线程数和最大线程数可根据计算机核数来定,我这里只是举个例子
// 核心线程池大小
private int corePoolSize = 50;
// 最大可创建的线程数
private int maxPoolSize = 200;
// 队列最大长度
private int queueCapacity = 1000;
// 线程池维护线程所允许的空闲时间
private int keepAliveSeconds = 300;
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
使用时:
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
public void uploadFiles(Map<String, SysUser> map, List<SysUser> userSaveList,List<File> fileSaveList) throws Exception {
if (CollectionUtils.isEmpty(fileSaveList)) {
return;
}
CountDownLatch latch = new CountDownLatch(fileSaveList.size());
log.info("开始进行任务处理——开启多线程处理,核心线程:{} ,最大线程:{} !", threadPoolTaskExecutor.getCorePoolSize(),threadPoolTaskExecutor.getMaxPoolSize());
for (File file : fileSaveList) {
String fileNameOnly = file.getName();
String name = fileNameOnly.substring(fileNameOnly.lastIndexOf("/") + 1, fileNameOnly.lastIndexOf("."));
SysUser userUpdate = map.get(name);
SysUser sysUserSave = new SysUser();
sysUserSave.setUserId(userUpdate.getUserId());
sysUserSave.setUserType(userUpdate.getUserType());
//上传文件
String suffix = file.getName().substring(file.getName().lastIndexOf("."));
FileInputStream inputStream = new FileInputStream(file);
threadPoolTaskExecutor.execute(()->{
try {
String path = FileUploadUtils.upload(Constants.MINIO_PCBFILE_BUCKET, "avatar/" + sysUserSave.getUserType() + "/" + fileNameOnly, inputStream, ContentType.getContentType(suffix.toLowerCase()));
inputStream.close();
sysUserSave.setAvatar(path);
userSaveList.add(sysUserSave);
latch.countDown();
int j = fileSaveList.size() / 10;
if (latch.getCount() % (j) == 0) {
log.info("处理度:{} %",latch.getCount() / (j)*10);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
latch.await();
log.info("所有任务都完成!");
}
//处理完之后
log.info("-------------- 总共花费 {} s ,成功更新 {} 个头像--------------", Duration.between(start, end).getSeconds(), userSaveList.size());
其中 CountDownLatch时是为了实现线程同步,所有上传文件线程跑完了之后才执行主线程!类似于操作系统pv操作
CountDownLatch(int count); //构造方法,创建一个值为count 的计数器。
await();//阻塞当前线程,将当前线程加入阻塞队列。
await(long timeout, TimeUnit unit);//在timeout的时间之内阻塞当前线程,时间一过则当前线程可以执行,
countDown();//对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。
关于核心线程数
网传核心线程数标准为
根据当前业务是IO密集型还是CPU密集型,设置核心线程数
CPU密集型:核心线程数 = CPU核数 + 1
IO密集型:核心线程数 = CPU核数 * 2
但我电脑是6核,核心线程数设置为12时上传700个文件用了39秒,设置为50时用了19秒,然后核心线程数目为100时,耗费17s。好像跟这个差距有点大。所以也不知道这个标准对不对了。
//核心线程数为12
- -------------- 总共花费 39 s ,成功更新 698 个头像--------------
//核心线程数为50
- -------------- 总共花费 19 s ,成功更新 698 个头像--------------
//核心线程数为100
- -------------- 总共花费 17 s ,成功更新 698 个头像--------------