最近在项目中有一个需求是从文件服务器上下载投标文件然后打包返回给前台下载,由于文件比较多,单独一个个去下载非常的慢,很耗时,导致浏览器半天没反应。
为了提高效率,于是对下载这一块打算开启多线程去执行。因为要等子线程所有文件都下载完成再打包所有主线程需要等待子线程都完成才执行。当时想到了两种方法,第一种是实现Callable接口,可以都到返回值,根据返回值的情况判断子线程是否都执行完成;第二种是使用CountDownLatch,子线程中使用countDown()方法控制子线程是否执行完成。我选择的使用CountDownLatch,接下来主要讲解一下怎么使用
1.创建CountDownLatch对象,设置子线程的数量
countDownLatch countDownLatch = new CountDownLatch(size);
其中size就是子线程的数量
2.开启线程执行方法
bidOpenProcessAttachDTO.stream().forEach(e -> {
new Thread(new Runnable() {
@Override
public void run() {
/** 子线程执行的方法 */
try {
Thread.sleep(1000);
String res = HttpUtil.get(sysInfoProperties.getFileSysURL() + (sysInfoProperties.getFileSysURL().endsWith("/") == true ? "" : "/") + "download?attachGroup=" + e.getAttachGroup() + "&attachPath=" + e.getAttachPath());
Result retRes = JSONUtil.toBean(res, Result.class);
if (!retRes.isSuccess()) {
throw new BootException(TradeErrorCodeEnum.INTERFACE_RETURN_ERROR);
}
String result = (String) retRes.getResult();
long size = HttpUtil.downloadFile(result, FileUtil.newFile(tempPath_ + File.separator + e.getUnitName() + "-" + e.getAttachName() + "." + e.getAttachSuffix()));
if (size < 0) {
throw new BootException(TradeErrorCodeEnum.ZBWJ_DOWNLOAD_FAIL);
}
} catch (InterruptedException e) {
throw new BootException(e.getMessage());
} finally {
countDownLatch.countDown(); //每一个子线程执行完调用此方法使计数减一
}
}
}).start();
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new BootException(e.getMessage());
}
//压缩生成.tbwj
File bidFile = ZipUtil.zip(tempPath_, bizFilePath + File.separator + "TBWJ" + File.separator + param.getBidSectionName() + File.separator + param.getBidSectionName() + CommonConstant.ZIP);
//删除文件
FileUtil.del(tempPath + File.separator + "TBWJ" + File.separator + param.getBidSectionName());
return bidFile;
讲一下countDownLatch.countDown()方法,因为new CountDownLatch(size)时会产生一个计数,初始值是size,当调用countDown()方法时,size减一,当size变成0时,await()方法就会唤醒主线程,然后主线程执行。另外,await()的作用是使当前线程休眠,发生以下两种情况之一就会结束休眠,1.使用countDown()直到size为0 2.其他线程中断当前线程
另外,countDownLatch 可以配合线程池一起使用
private static ExecutorService cachedThreadPool = Executors.newFixedThreadPool(3);
public File queryBidFileByBidSectionId(BidOpenColseExportParam param, HttpServletRequest request, HttpServletResponse response) {
List<BidOpenProcessAttachDTO> bidOpenProcessAttachDTO = bidOpenCloseMapper.queryBidFileByBidSectionId(param.getBidSectionId(), BusinessTypeEnum.DECLASSIFIED_TENDER.getCode());
if (Objects.isNull(bidOpenProcessAttachDTO)) {
throw new BootException(TradeErrorCodeEnum.SEARCH_OBJ_DO_NOT_EXSISTS.getDesc());
}
//循环将文件存到临时路径
String tempPath_ =tempPath + File.separator + "TBWJ" + File.separator + param.getBidSectionName();
Thread.currentThread().setName("主线程");
log.info("开始处理下载任务>>>{}");
long startTime = System.currentTimeMillis();
CountDownLatch countDownLatch = new CountDownLatch(bidOpenProcessAttachDTO.size());
bidOpenProcessAttachDTO.stream().forEach(e -> {
cachedThreadPool.execute(() -> {
String res = HttpUtil.get(sysInfoProperties.getFileSysURL() + (sysInfoProperties.getFileSysURL().endsWith("/") == true ? "" : "/") + "download?attachGroup=" + e.getAttachGroup() + "&attachPath=" + e.getAttachPath());
Result retRes = JSONUtil.toBean(res, Result.class);
if (!retRes.isSuccess()) {
throw new BootException(TradeErrorCodeEnum.INTERFACE_RETURN_ERROR);
}
String result = (String) retRes.getResult();
long size = HttpUtil.downloadFile(result, FileUtil.newFile(tempPath_ + File.separator + e.getUnitName() + "-" + e.getAttachName() + "." + e.getAttachSuffix()));
if (size < 0) {
throw new BootException(TradeErrorCodeEnum.ZBWJ_DOWNLOAD_FAIL);
}
countDownLatch.countDown();
log.info(Thread.currentThread().getName() + "运行完成");
});
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new BootException(e.getMessage());
}
log.info(Thread.currentThread().getName() + "运行");
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
log.info("下载完成,用时>>>" + time);
//压缩生成.tbwj
File bidFile = ZipUtil.zip(tempPath_, bizFilePath + File.separator + "TBWJ" + File.separator + param.getBidSectionName() + File.separator + param.getBidSectionName() + CommonConstant.ZIP);
//删除文件
FileUtil.del(tempPath + File.separator + "TBWJ" + File.separator + param.getBidSectionName());
return bidFile;
}