1. DateUtil 日期工具类设计与实现
在Java后端开发中,日期处理是最基础却最容易出问题的环节之一。我见过太多项目因为日期处理不当导致的线上事故——从简单的格式转换异常到跨时区的业务逻辑错误。今天要分享的这个DateUtil工具类,是我在多个Spring Boot项目中迭代优化后的成果,它解决了日期处理中的三个核心痛点:线程安全、性能开销和API易用性。
这个工具类的特别之处在于:
- 采用双重检查锁+ThreadLocal实现线程安全的SimpleDateFormat缓存
- 同时支持传统的Date/Calendar和Java 8的LocalDate两套API
- 封装了实际业务中最常用的日期操作方法
- 性能比直接new SimpleDateFormat提升约3-5倍(JMH基准测试结果)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心设计解析
2.1 线程安全实现机制
先看最关键的线程安全问题。SimpleDateFormat不是线程安全的这个坑,几乎每个Java开发者都踩过。常规做法要么每次new实例(性能差),要么加synchronized锁(并发能力差)。本工具类采用的解决方案堪称教科书级别的典范:
java复制private static final Object lockObj = new Object();
private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<>();
private static SimpleDateFormat getSdf(final String pattern) {
ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
if (tl == null) {
synchronized (lockObj) { // 双重检查锁
tl = sdfMap.get(pattern);
if (tl == null) {
tl = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
sdfMap.put(pattern, tl);
}
}
}
return tl.get();
}
这个设计有几个精妙之处:
- 使用ThreadLocal确保每个线程有自己的SimpleDateFormat实例
- 通过pattern作为key支持多种日期格式
- 双重检查锁避免并发时重复创建
- 静态Map缓存所有pattern对应的ThreadLocal
重要提示:这里lock
