共享SimpleDateFormat的并发问题

1、问题提出

梳理订单逻辑时发现对日期格式进行format的代码有如下写法
共享SimpleDateFormat的并发问题_第1张图片

共享SimpleDateFormat的并发问题_第2张图片

OneDateUtil中定义了一个全局static的SimpleDateFormat对象。SimpleDateFormat对象不是线程安全的,在多线程环境下容易造成数据转换和处理错误

2、为什么SimpleDateFormat线程不安全

SimpleDateFormat在类的注释中标明了并不适合多线程场景
共享SimpleDateFormat的并发问题_第3张图片
深入排查,会发现SimpleDateFormat中的format方法中,会将输入的日期给calendar赋值,由于SimpleDateFormat被定义为static类型的,可以被多个线程访问到。当a线程执行了calendar.setTime(date)后,b线程又执行了calendar.setTime(date),此时a线程未执行结束,a线程中的calendar就会被覆盖
共享SimpleDateFormat的并发问题_第4张图片
共享SimpleDateFormat的并发问题_第5张图片
SimpleDateFormat的parse方法有同样的问题,多线程下执行cal.clear()会出现线程安全问题
共享SimpleDateFormat的并发问题_第6张图片
共享SimpleDateFormat的并发问题_第7张图片

3、如何修改

方案1:加锁 synchronized修饰,影响性能,禁止。

方案2:局部变量方式,项目中也存在该使用方式,高并发下会创建大量的SimpleDateFormat类对象,不推荐。
共享SimpleDateFormat的并发问题_第8张图片
方案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中进行处理,从而避免了线程安全的问题
共享SimpleDateFormat的并发问题_第9张图片

4、风险点

1、前端传的日期非正规日期格式
共享SimpleDateFormat的并发问题_第10张图片
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;

你可能感兴趣的:(工作总结,java,开发语言)