You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Predicate处理日期时出现线程安全的ArrayIndexOutOfBoundsException问题

根因定位

你遇到的偶发数组越界异常完全是 SimpleDateFormat 的线程安全问题导致的:

  • SimpleDateFormat 内部维护了一个共享的Calendar实例,调用format/parse方法时会修改该实例的状态,官方文档明确标注该类不适用于多线程并发场景
  • 你的代码中使用了 parallelStream() 并行流处理实体,多线程会同时调用成员变量 formatter 的format方法,Calendar内部状态被并发篡改,最终触发ArrayIndexOutOfBoundsException

验证方法

将 entities.parallelStream() 改为 entities.stream() 串行执行,重新测试,异常会彻底消失,即可100%确认是该问题。

修复方案

推荐按优先级选择以下方案:

  1. Java 8+优先使用DateTimeFormatter(最优)
    DateTimeFormatter是JDK8新增的日期格式化类,天生线程安全,可直接作为静态全局变量使用,无并发风险:
    // 定义全局格式化器
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy")
            .withZone(ZoneId.systemDefault());
    
    // Predicate中调用,Date对象先转Instant
    predicates.add(new SimpleEntry<>("date", entity -> entity.getDate() != null 
        && dateStr.equalsIgnoreCase(DATE_FORMATTER.format(entity.getDate().toInstant()))));
    
  2. 用ThreadLocal封装SimpleDateFormat(兼容低版本JDK)
    每个线程持有独立的SimpleDateFormat实例,避免重复创建的开销,同时解决并发问题:
    private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER = ThreadLocal.withInitial(() -> {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        sdf.setTimeZone(TimeZone.getDefault());
        return sdf;
    });
    
    // 调用时使用
    DATE_FORMATTER.get().format(entity.getDate())
    
  3. 局部创建SimpleDateFormat实例(最简单)
    直接在Predicate的lambda内部每次创建新的SimpleDateFormat实例,逻辑最直白,适合性能要求不高的场景:
    predicates.add(new SimpleEntry<>("date", entity -> {
        if (entity.getDate() == null) return false;
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        sdf.setTimeZone(TimeZone.getDefault());
        return dateStr.equalsIgnoreCase(sdf.format(entity.getDate()));
    }));
    

内容的提问来源于stack exchange,提问作者Dogukan Evcil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 07:36:02