并行流中用ThreadLocal封装DecimalFormat仍结果不一致问题排查
问题根源:累加操作的竞态条件
你的ThreadLocal封装DecimalFormat的写法是正确的,已经解决了它的线程安全问题。结果不一致的原因出在并行流中AtomicReference的累加逻辑不是原子操作:
sum6.set(sum6.get() + val) 实际是三步拆分操作:
- 获取当前sum值(
sum6.get()) - 计算新的sum值(
当前值 + val) - 设置新的sum值(
sum6.set(...))
在并行流中,多个线程可能同时执行第一步,拿到相同的原始sum值,随后各自计算并覆盖设置,导致部分累加值丢失,最终结果和串行计算不一致。
正确的解决方式
方式1:使用AtomicReference的原子更新方法
用accumulateAndGet方法替代手动get+set,它会原子性地完成“获取-计算-更新”全流程:
AtomicReference<Double> sum6 = new AtomicReference<>(0.0); numberList.stream().parallel().forEach(aDouble -> { double val = Double.parseDouble(FORMATTER.get().format(aDouble)); sum6.accumulateAndGet(val, (current, add) -> current + add); });
方式2:使用Stream API原生sum方法(更推荐)
直接利用并行流的mapToDouble+sum(),内部已经处理了线程安全的累加逻辑,代码更简洁规范:
double sum6 = numberList.stream().parallel() .mapToDouble(aDouble -> Double.parseDouble(FORMATTER.get().format(aDouble))) .sum();
额外验证:DecimalFormat的线程安全问题
如果想验证未用ThreadLocal时的线程安全问题,可以把并行流里的FORMATTER.get()换成共享的df,多次运行会发现结果不仅和串行不一致,甚至每次运行的结果都不同——这才是DecimalFormat线程不安全导致的问题,而你当前的ThreadLocal用法已经规避了这个风险。
内容的提问来源于stack exchange,提问作者Madhur Ahuja
相关产品推荐
相关产品推荐

