美文网首页
Java随笔(1) 线程池使用异常

Java随笔(1) 线程池使用异常

作者: sunyelw | 来源:发表于2020-03-21 11:41 被阅读0次

昨天晚上定位个问题花了好久,诶,还是菜啊~


先上代码(稍作修改)

private <T> List<T> parseFile(String fileName, String filePath, int number, List<String> errList) {

    List<T> userList = new ArrayList <>(number);
    String nowPath = filePath + "/" + fileName;

    // es
    int ths = TaskUtil.getThs(number, EVERY_SIZE);
    ExecutorService tape = TaskUtil.getEs(ths, "parse-" + fileName);
    try (BufferedReader bs =
                 new BufferedReader(
                         new InputStreamReader(
                                 new FileInputStream(nowPath), "GBK"))) {
        String line;
        while ((line = bs.readLine()) != null) {
            String finalLine = line;
            tape.execute(() -> {
                T bu;
                if ((bu = parseLine(finalLine, errList)) != null) userList.add(bu);
            });
        }
        return userList;
    } catch (Exception e) {
        log.error("ag_error_parse_file filePath: {}", nowPath, e);
        return new ArrayList <>();
    } finally {
        tape.shutdown();
    }
}

需求描述:多线程解析一个文件,存到一个List中返回。
前提:不想显示定义线程类,比如Callable或Runnable的实现,减少代码量
问题描述:解析没问题,但返回的List时空时不空。

执行流程简化

  • 创建线程池
  • 循环将任务加入线程池
  • 结果塞入集合中
  • 返回这个集合

这里总共有两个致命问题

  • ArrayList 不是一个线程安全的集合,在这种情况下使用存在并发风险
  • 返回集合时,线程池可能还没执行结束,导致返回的集合中数据不全

改动一下

private <T> List<T> parseFile(String fileName, String filePath, int number, List<String> errList) {

    List<T> userList = Collections.synchronizedList(new ArrayList <>());
    String nowPath = filePath + "/" + fileName;

    // es
    int ths = TaskUtil.getThs(number, EVERY_SIZE);
    ExecutorService tape = TaskUtil.getEs(ths, "parse-" + fileName);
    try (BufferedReader bs =
                 new BufferedReader(
                         new InputStreamReader(
                                 new FileInputStream(nowPath), "GBK"))) {
        String line;
        while ((line = bs.readLine()) != null) {
            String finalLine = line;
            tape.execute(() -> parseLine(finalLine, errList, userList));
        }
        tape.shutdown();
        while (true) {
            if (tape.isTerminated()) {
                return userList;
            }
            Thread.sleep(100);
        }
    } catch (Exception e) {
        log.error("ag_error_parse_file filePath: {}", nowPath, e);
        return new ArrayList <>();
    } finally {
        if (!tape.isTerminated()) tape.shutdownNow();
    }
}

针对以上两点做的改动

  • List 改成线程安全的集合,原理是使用synchronized关键字对集合操作上锁,类似Vector(在某些情况这种方式仍不够安全,需要使用CopyOnWriteArrayList
  • 执行流程改为等线程池结束后再返回目标集合,确保了数据的完整

我看过ArrayList的源码,也看过 ThreadPoolExecutor 的实现,但还是会犯这种"低级错误",这提醒我敲代码时应该要更加细心点,多想想,谋定而后动。

吾日三省。

相关文章

  • Java随笔(1) 线程池使用异常

    昨天晚上定位个问题花了好久,诶,还是菜啊~ 先上代码(稍作修改) 需求描述:多线程解析一个文件,存到一个List中...

  • 线程

    Java 并发编程:线程池的使用 Java 并发编程:线程池的使用java 多线程核心技术梳理 (附源码) 本文对...

  • Java线程池

    Java并发编程:线程池的使用 1.使用线程池的优点?1.降低资源消耗:重复利用线程池中的线程节省线程创建和销毁带...

  • 【JAVA提升】- 线程、线程池、并发包(3)

    1.线程池(java.util.concurrent) 为什么要有线程池 ? 我们知道。使用线程来处理任务,可以达...

  • Java中的线程池

    Java提供的线程池出自并发大师Doug Lea之手,向大师致敬! 1. 为什么要用线程池 合理使用线程池可以带来...

  • 线程池设计

    使用线程池的场合 单个任务处理时间短 将需处理的任务数量大 使用Java线程池好处 1、使用new Thread(...

  • 谈谈我对Java线程的了解

    本篇文章主要介绍java中线程和线程池的使用 一、线程 1. 线程分类 继承 java.lang.Thread 实...

  • Java线程池解析

    参考文章:Java并发:线程池,饱和策略 前言 Java线程池的使用在工作中还是比较常见的,线程池可以减小线程建立...

  • python3 线程池和异常处理

    引用 线程池的基本使用as_completedwaitmap 线程池的异常处理 进程池用法 引用 Python中已...

  • 零碎知识点整理

    sofa结构体系:多模块 sofa中使用sofa线程池 Java 线程池的使用非常广泛,目前有两类广泛使用的线程池...

网友评论

      本文标题:Java随笔(1) 线程池使用异常

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