zoukankan      html  css  js  c++  java
  • 一处隐蔽的空指针异常

    记录一个遇到的隐蔽的空指针异常。

    公司里的测试同事之前发现项目里有处偶现的空指针异常。

    大致的代码是这样的:

    private void doWriteTransDetails(List<TransDetailDTO> transDetails) {
      Map<String, List<TransDetailDTO>> transDetailsInvestorMap =
    transDetails.stream().collect(Collectors.groupingBy(item -> item.getInvestor().getName()));

    // 省略
      ......
    }

    对于固定的测试集,多次重跑偶现空指针出现在groupingBy中,乍一看推断是item.getInvestor()为null导致getName()出现空指针。但经过debug查看,实际上是偶现item为null的情况,对于固定的测试集,不应该有这样的偶然因素。

    测试猜测是否是因为多线程环境造成的线程安全问题?但这个list在最上游是在方法中定义的,封闭在栈中,不可能有多个线程操作它。

    经过排查,终于在上游代码中某处发现了元凶。

    public void processTransAppLyList(List<TransDetailDTO> transDetails, List<TransApplyDTO> transApplys) {
      // 省略
      ......
    transApplys.parallelStream().forEach(trans -> {
    TransDetailDTO transDetailDTO = new TransDetailDTO();
    // 省略
         ......
    transDetails.add(transDetailDTO);
    });
      // 下游调用省略
      ......
    }

    源头就是因为此处代码用了并行流,并行流是基于Fork/Join框架实现的。直白点说,这里`transApplys.parallelStream().forEach`是一个多线程的并发环境,对一个`transDetails`进行add操作具有不可预期的结果,可能会数组越界,也可能会元素丢失,也可能会部分index的引用为null。

    由于此处数据量不是太大,且操作复杂度不高,直接把并行流改为普通流即可修复。

    这个比较隐蔽的空指针异常的经验教训是:开发者在考虑用并行流的时候需要再仔细想一下,有必要吗?甚至可以考虑一下有必要用流吗?如果用并行流仍然是需要注意线程安全问题,不要被笼统的`lambda表达式/stream天然是线程安全的`给误导了。





  • 相关阅读:
    (转)关于IBM小机P520的面板使用
    (转)mysql的sql_mode合理设置
    (转)Mysql技术内幕InnoDB存储引擎-事务&备份&性能调优
    杨辉三角
    异或的陷阱(转)
    通过数组看栈堆
    数组的一些知识点和插入选择冒泡排序
    运算符
    Java的变量相关
    有符号的数据表示法(原、反、补)
  • 原文地址:https://www.cnblogs.com/micrari/p/6523508.html
Copyright © 2011-2022 走看看