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

Java中DecimalFormat解析偶发空字符串NumberFormatException求助

问题分析:DecimalFormat.parse()偶尔抛出空字符串NumberFormatException

问题背景

在Java 11.0.20+8-LTS-sapmachine环境下,一段数字解析代码偶尔会抛出java.lang.NumberFormatException: For input string: ""异常。

最初误提供的代码

用户最初误以为使用的是如下代码:

static Number parse(String s) {
    if (s == null || s.trim().length() == 0) {
      return 0;
    }
    return Long.parseLong(s);
}

用户疑问:既然已经通过s.trim().length() != 0判断字符串非空,空字符串是如何传入parseLong()的?

更新后的实际代码

用户补充了关键信息:实际使用DecimalFormat进行解析,代码如下:

static java.text.DecimalFormat df = new java.text.DecimalFormat("#");

static Number parse(String s) throws java.text.ParseException {
    if (s == null || s.trim().length() == 0) {
        return 0;
    }
    return df.parse(s);
}

异常堆栈信息

生产环境中抛出的异常堆栈:

java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Long.parseLong(Long.java:702)
    at java.lang.Long.parseLong(Long.java:817)
    at java.text.DigitList.getLong(DigitList.java:195)
    at java.text.DecimalFormat.parse(DecimalFormat.java:2121)
    at java.text.NumberFormat.parse(NumberFormat.java:429)

异常原因分析

  1. DecimalFormat非线程安全:DecimalFormat是非线程安全的类,代码中将其声明为static全局变量,当多个线程同时调用parse()方法时,会导致该实例的内部解析状态被并发修改,出现混乱。比如一个线程正在解析某字符串时,另一个线程的操作可能导致内部缓冲区被重置或覆盖,最终使得DigitList.getLong()收到空字符串输入,触发Long.parseLong("")抛出异常。
  2. trim()判断不覆盖所有异常场景:虽然s.trim().length() == 0能过滤掉空白字符串,但DecimalFormat对某些非空白字符串的解析仍可能触发内部逻辑错误(不过此次异常的核心原因还是多线程下的状态破坏)。

解决方案

  • 使用ThreadLocal隔离线程实例:为每个线程分配独立的DecimalFormat实例,避免并发状态干扰:
    private static final ThreadLocal<java.text.DecimalFormat> DF_THREAD_LOCAL = 
        ThreadLocal.withInitial(() -> new java.text.DecimalFormat("#"));
    
    static Number parse(String s) throws java.text.ParseException {
        if (s == null || s.trim().isEmpty()) {
            return 0;
        }
        return DF_THREAD_LOCAL.get().parse(s);
    }
    
  • 每次调用创建新实例:如果解析频率不高,也可以在每次调用parse()时创建新的DecimalFormat对象,避免线程安全问题。
  • 同步锁(不推荐):在调用df.parse()时加同步锁,会导致多线程场景下性能下降,仅适合低并发场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 02:16:07