目录
一、SimpleDateFormat存在的线程安全问题
SimpleDateFormat线程不安全,是因为类中的calendar本身线程不安全
二、解决方案
1. 局部变量方式(不推荐)
每次格式化时间时都创建SimpleDateFormat对象。这方案虽然解决了线程安全问题,但会导致创建了大量SimpleDateFormat对象,会浪费内存和性能,所以不推荐在高并发场景使用。
代码示例:
public class DateUtil {
public static String format(Date date, String format) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
return simpleDateFormat.format(date);
}
}
2. ThreadLocal方式
通过ThreadLocal保存各个线程的SimpleDateFormat对象的副本,使得每个线程都使用自身的SimpleDateFormat对象,而达到互不干扰,同时执行性能也比较高,推荐使用。
代码示例:
public class DateUtil {
/**
* 常规日期时间格式,24小时制yyyy-MM-dd HH:mm:ss
*/
public static final String NORMAL_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* 常规日期,yyyy-MM-dd
**/
public static final String NORMAL_DATE_FORMAT = "yyyy-MM-dd";
/**
* 锁对象
*/
private static final Object LOCK_OBJ = new Object();
/**
* 存放不同的日期模板格式的SimpleDateFormat的Map
*/
private static Map<String, ThreadLocal<SimpleDateFormat>> threadLocalMap = new HashMap<>(8);
/**
* 返回一个ThreadLocal的SimpleDateFormat,每个线程只会new一次
*
* @param pattern
* @return
*/
private static SimpleDateFormat getDateFormat(final String pattern) {
ThreadLocal<SimpleDateFormat> tl = threadLocalMap.get(pattern);
// 双重判断和同步,防止threadLocalMap这个单例被多次重复的SimpleDateFormat
if (tl == null) {
synchronized (LOCK_OBJ) {
tl = threadLocalMap.get(pattern);
if (tl == null) {
tl = ThreadLocal.withInitial(() -> new SimpleDateFormat(pattern));
threadLocalMap.put(pattern, tl);
}
}
}
return tl.get();
}
/**
* 日期转字符串
*
* @param date
* @param format
* @return
*/
public static String format(Date date, String format) {
return getDateFormat(format).format(date);
}
/**
* 字符串转日期
*
* @param dateStr
* @param format
* @return
* @throws ParseException
*/
public static Date parse(String dateStr, String format) throws ParseException {
return getDateFormat(format).parse(dateStr);
}
}
3. DateTimeFormatter方式
DateTimeFormatter是Java 8提供的专门处理日期和时间的类,线程安全,同时性能也不错。如果项目广泛使用Date和Calendar,是需要转换后使用的,所以自行考虑选择使用。
代码示例:
public class DateUtil {
public static final DateTimeFormatter NORMAL_DATETIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String format(LocalDate localDate, DateTimeFormatter formatter) {
return formatter.format(localDate);
}
public static void main(String[] args) {
System.out.println(DateUtil.format(LocalDate.now(), NORMAL_DATETIME_FORMAT));
}
}
4. joda-time方式
一个第三方日期时间处理的类库,线程安全,经过高并发场景考验,推荐使用。
引入依赖:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.10</version>
</dependency>
代码示例:
DateTimeFormatter dtfd = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dt6 = DateTime.parse("2022-11-20 7:20:45", dtf);
个人比较推荐使用ThreadLocal方式或joda-time方式