simpledateformat 不是线程安全的,多线程共享会导致格式化错乱或异常;应优先使用线程安全的 datetimeformatter,次选 threadlocal 封装或局部创建实例。

SimpleDateFormat 本身不是线程安全的,不能在多线程中直接共享使用。它的内部依赖可变的 Calendar 和解析位置(pos)等状态,多个线程同时调用 format() 或 parse() 会相互干扰,导致格式化错乱、解析失败甚至抛出异常。
避免共享静态实例
最常见错误是把 SimpleDateFormat 声明为 static final 字段供全局复用。这种写法看似节省对象创建开销,实则埋下并发隐患。只要被多个线程同时访问,就可能出问题——哪怕只是读操作,因为 format 和 parse 都会修改内部状态。
- 不要这样写:private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
- 也不要把它作为工具类的单例字段暴露出去
推荐方案:优先用 DateTimeFormatter(Java 8+)
这是目前最简洁、安全且现代化的替代方式。DateTimeFormatter 是不可变的、线程安全的,支持所有常用模式,且与 LocalDate、LocalDateTime 等新日期类型天然适配。
- 创建方式:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - 格式化:
String s = LocalDateTime.now().format(formatter); - 解析:
LocalDateTime dt = LocalDateTime.parse("2026-07-28 15:30:00", formatter);
兼容旧代码:用 ThreadLocal 封装 SimpleDateFormat
如果项目仍大量使用 Date 和 SimpleDateFormat,又无法立即升级到 java.time,ThreadLocal 是平衡性能与安全的优选方案。每个线程独享一个实例,无竞争、无锁、开销小。
- 典型写法:
private static final ThreadLocal<simpledateformat> SDF = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));</simpledateformat> - 使用时:
SDF.get().format(new Date()),用完无需手动清理(ThreadLocal 在线程结束时自动回收)
简单场景:直接创建局部变量
若格式化频率不高、吞吐量不大,或方法调用频次可控,直接在方法内 new 一个 SimpleDateFormat 是最直观、无副作用的做法。
- 优点:逻辑清晰,完全规避共享风险
- 注意:避免在循环体内反复 new(可提到循环外),但单次调用无需顾虑
- 示例:
String s = new SimpleDateFormat("yyyy-MM-dd").format(date);
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











