simpledateformat 非线程安全,多线程共享易导致解析错误;应避免静态复用,推荐使用 threadlocal 封装或直接改用线程安全的 datetimeformatter。

SimpleDateFormat 是 Java 中常用的日期格式化工具,但它不是线程安全的。多个线程共用同一个 SimpleDateFormat 实例时,容易出现解析错误、格式混乱甚至抛出异常(如 java.lang.NumberFormatException 或返回错误日期),比如把 "2023-01-01" 解析成 "2023-12-01" 或 "1970-01-01"。
避免共享 SimpleDateFormat 实例
最直接有效的做法是:每个线程使用独立的 SimpleDateFormat 实例,不跨线程复用。
- 在方法内部创建局部变量,用完即弃(适合调用不频繁的场景)
- 使用
ThreadLocal<simpledateformat></simpledateformat>管理,确保每个线程持有自己的副本
示例(ThreadLocal 方式):
<code>private static final ThreadLocal<simpledateformat> DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));</simpledateformat></code>
使用时调用 DATE_FORMAT.get().parse(...) 或 format(...) 即可。注意:若需重用或设置时区、语言环境,应在初始化时统一配置。
改用 java.time 包中的线程安全类型
Java 8 引入的 java.time API(如 DateTimeFormatter、LocalDateTime)默认线程安全,推荐作为长期替代方案。
-
DateTimeFormatter是不可变且线程安全的,可定义为static final - 配合
LocalDateTime.parse(..., formatter)或formatter.format(...)使用
示例:
<code>private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");</code>
无需额外同步或 ThreadLocal,简洁又可靠。
加锁同步(仅作临时兼容方案)
若项目暂无法升级或重构,可在调用 parse/format 的关键代码块加同步锁。
- 对 SimpleDateFormat 实例加 synchronized(粒度较粗,影响并发性能)
- 或使用
synchronized (dateFormat) { ... }显式同步
不建议在高并发场景下长期使用,仅用于快速修复紧急问题。
检查并清理已有共享实例
排查代码中是否将 SimpleDateFormat 声明为 static 成员变量或 Spring Bean(prototype 除外),这类写法极易引发问题。
- Spring 中避免
@Bean默认 scope 的 SimpleDateFormat - 静态工具类中不要暴露可变的 SimpleDateFormat 实例
- 使用 IDE 全局搜索
SimpleDateFormat+static快速定位风险点
发现后优先替换为 DateTimeFormatter 或改用 ThreadLocal 封装。










