美文网首页
上传并异步解析excel

上传并异步解析excel

作者: 奶盐味小圆饼 | 来源:发表于2020-04-23 18:26 被阅读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));

相关文章

网友评论

      本文标题:上传并异步解析excel

      本文链接:https://www.haomeiwen.com/subject/ooaaihtx.html