上传并异步解析excel
2020-04-23 本文已影响0人
奶盐味小圆饼
需求描述: 文件上传之后返回上传成功结果, 异步解析文件并进行数据处理
第一种实现方式
线程池
- 本质上是一种对象池,用于管理线程资源。
- 在任务执行前,需要从线程池中拿出线程来执行。
- 在任务执行完成之后,需要把线程放回线程池。
- 通过线程的这种反复利用机制,可以有效地避免直接创建线程所带来的坏处。
@RestController
@RequestMapping("/workflow/v1/")
public class OldCodeHandleController extends BaseController {
public final ExecutorService executor = Executors.newSingleThreadExecutor();
@PreDestroy
public void destroy() {
logger.info("执行销毁逻辑,释放线程池");
// 停止接收新的解析任务
executor.shutdown();
// 超时关闭线程池
try {
boolean isTermination = executor.awaitTermination(1, TimeUnit.MINUTES);
if (!isTermination) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
/**
* 文件上传
*
* @param file
* @return
*/
@PostMapping("/excel/upload")
public BaseResponse excelUpload(@RequestBody MultipartFile file) throws IOException {
executor.submit( () -> oldCodeHandleService.uploadFile(file));
return ResponseHelper.createSuccessResponse("文件上传成功");
}
}
newSingleThreadExecutor
- Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!
Executors
- Executors是一个线程池工厂,提供了很多的工厂方法,我们来看看它大概能创建哪些线程池。
创建单一线程的线程池
创建单一线程的线程池
public static ExecutorService newSingleThreadExecutor();
- 顾名思义,这个线程池只有一个线程。若多个任务被提交到此线程池,那么会被缓存到队列(队列长度为Integer.MAX_VALUE)。当线程空闲的时候,按照FIFO的方式进行处理。
关闭线程池
-
在线程池使用完成之后,我们需要对线程池中的资源进行释放操作,这就涉及到关闭功能。我们可以调用线程池对象的shutdown()和shutdownNow()方法来关闭线程池。
-
这两个方法都是关闭操作,又有什么不同呢?
-
shutdown()会将线程池状态置为SHUTDOWN,不再接受新的任务,同时会等待线程池中已有的任务执行完成再结束。
-
shutdownNow()会将线程池状态置为SHUTDOWN,对所有线程执行interrupt()操作,清空队列,并将队列中的任务返回回来。
-
另外,关闭线程池涉及到两个返回boolean的方法,isShutdown()和isTerminated,分别表示是否关闭和是否终止。
第二种实现方式
用CompletableFuture来实现异步操作
@PostMapping("/excel/upload")
public BaseResponse excelUpload(@RequestBody MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
InputStream fileIs = file.getInputStream();
CompletableFuture.runAsync(() -> oldCodeHandleService.readExcel(fileIs, fileName));
return ResponseHelper.createSuccessResponse("文件上传成功");
}
CompletableFuture的runAsync
- 返回一个新的CompletableFuture,该任务在运行给定操作后由{@link ForkJoinPool#commonPool()}中运行的任务异步完成。
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(ASYNC_POOL, runnable);
}
- CompletableFuture的runAsync只是简单的异步执行一个线程,但是它将返回一个CompletableFuture,有了这个CompletableFuture,可以重新组装和调配,这是和一个普通Runnable不同之处。
第三种实现方式
实现Runnable接口复写run方法
public class AsyncTask implements Runnable {
private final HandleService handleService;
private final InputStream fileIs;
private final String fileName;
public AsyncTask(HandleService handleService, InputStream fileIs, String fileName) {
this.handleService =handleService;
this.fileIs = fileIs;
this.fileName = fileName;
}
@Override
public void run() {
handleService.readExcel(fileIs, fileName);
}
}
创建线程池
private ExecutorService executorService = Executors.newFixedThreadPool(10);
- 创建一个线程池,该线程池重用在共享的无边界队列上运行的固定数量的线程。在任何时候,最多{@code nThreads}个线程将是活动的处理任务。如果在所有线程都处于活动状态时提交了其他任务,则它们将在队列中等待线程可用。如果任何线程在关闭之前的执行过程中由于失败而终止,则在需要执行后续任务时将使用新线程来代替它。池中的线程将一直存在,直到显式地{@link ExecutorService#shutdown shutdown}。
向线程池提交任务
executorService.submit(new AsyncTask(handleService, fileIs, fileName));