若日志中出现NumberFormatException且堆栈含SimpleDateFormat.parse等关键词,基本可锁定为线程不安全导致的解析中断,需重点检查共享的SimpleDateFormat实例及并发调用场景。

直接看日志里异常堆栈中是否出现 NumberFormatException: For input string: "" 或类似提示,同时伴随 SimpleDateFormat.parse、DigitList.getLong、DecimalFormat.parse 等关键词——这基本可以锁定是线程不安全导致的解析中断。
检查 SimpleDateFormat 是否被多线程共享
重点排查以下几类代码模式:
- 静态 final 的 SimpleDateFormat 实例(如
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");),尤其在工具类、Service 类或 Controller 中被多个请求线程共用 - Spring Bean 中注入或声明为成员变量的 SimpleDateFormat(非 prototype 作用域)
- 被缓存复用的对象池、工厂类返回的 DateFormat 实例未做线程隔离
复现问题要抓“并发+重复调用”特征
线上偶发,本地难重现?试试这个轻量级验证方式:
- 写一个最小测试:启动 5–10 个线程,循环调用同一个 SimpleDateFormat 实例的
parse("2023-01-01")100 次以上 - 观察是否快速出现
NumberFormatException或解析出错(如 "2023-01-01" 变成 "2022-12-31") - 换成每次 new 一个新的 SimpleDateFormat,再跑同样逻辑——如果不再报错,就是线程安全问题无疑
定位具体调用链和上下文
从异常日志向上追溯三步:
- 看抛异常那一行是不是直接调用了
df.parse(...),且 df 是外部传入或类成员 - 检查该方法是否可能被异步线程、定时任务、CompletableFuture、@Async 方法调用
- 确认入参字符串是否固定(如常量时间字符串),若固定却解析失败,几乎可排除输入问题,直指状态污染
避免误判:区分真异常与假阳性
不是所有 NumberFormatException 都是线程安全问题,注意排除:
- 输入字符串本身含非法字符(如空格、中文横线、多余字母),但这类通常稳定复现,不随并发波动
- 格式模板与字符串不匹配(如用
"yyyy-MM-dd"去 parse"2023/01/01"),也会抛 NFE,但错误明确、可预测 - Calendar 字段越界(如月份写成 13)一般抛的是
IllegalArgumentException,不是 NFE
真正由线程不安全引发的 NumberFormatException,特点是:输入完全合法、单线程下 100% 成功、并发一上来就间歇性失败,且堆栈深入到 DigitList 或 DecimalFormat 内部——那是 Calendar 和 pos 状态被覆盖后,数字解析器读到了空字符串或截断字段所致。

















