1、问题提出
梳理订单逻辑时发现对日期格式进行format的代码有如下写法
OneDateUtil中定义了一个全局static的SimpleDateFormat对象。SimpleDateFormat对象不是线程安全的,在多线程环境下容易造成数据转换和处理错误。
2、为什么SimpleDateFormat线程不安全
SimpleDateFormat在类的注释中标明了并不适合多线程场景
深入排查,会发现SimpleDateFormat中的format方法中,会将输入的日期给calendar赋值,由于SimpleDateFormat被定义为static类型的,可以被多个线程访问到。当a线程执行了calendar.setTime(date)后,b线程又执行了calendar.setTime(date),此时a线程未执行结束,a线程中的calendar就会被覆盖
SimpleDateFormat的parse方法有同样的问题,多线程下执行cal.clear()会出现线程安全问题
3、如何修改
方案1:加锁 synchronized修饰,影响性能,禁止。
方案2:局部变量方式,项目中也存在该使用方式,高并发下会创建大量的SimpleDateFormat类对象,不推荐。
方案3:java 8 中引入新的日期类 API,这些类是不可变的,且线程安全的,推荐。
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) {
LocalDateTime time = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
return formatter.format(time);
}
DateTimeFormatter的format方式是将入参对象封装在一个新建的DateTimePrintContext对象中,作为入参传给printerParser对象,以局部变量的方式传入到方法中,而不是像SimpleDateFormat那样把日期对象设置到全局变量calendar中进行处理,从而避免了线程安全的问题
4、风险点
1、前端传的日期非正规日期格式
2、序列化问题
需要进行序列化的地方,可能存在问题,可以通过加注解解决
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime localDateTime ;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate localDate;