阿里java规范
阿里java规范第六节第5点提到SimpleDateFormat 是线程不安全的类,使用时需注意
Java 8 引入的 DateTimeFormatter 类替代了旧有的 SimpleDateFormat 类
两者的区别考虑和优势
线程安全性
-
SimpleDateFormat:不是线程安全的。如果多个线程同时访问同一个SimpleDateFormat实例进行日期和字符串的转换,可能会导致数据不一致或抛出异常。为了在多线程环境下安全使用,通常需要使用
ThreadLocal
为每个线程提供独立的SimpleDateFormat实例,或者通过其他同步机制来保证线程安全。 -
DateTimeFormatter:是线程安全的。它可以在多线程环境中被共享和重用,无需担心线程安全问题。DateTimeFormatter的不可变性(immutability)和内部设计使其在多线程环境下能够保持数据的完整性和一致性。
安全性是最大的区别,安全性区别主要体现在多线程情况下,下面通过代码示例验证安全性方面的区别:
DateTimeFormatter多线程下安全性代码演示
public static void main(String[] args) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 格式化日期时间
LocalDateTime now = LocalDateTime.now();
String formattedDateTime = dateTimeFormatter.format(now);
System.out.println("格式化后的日期时间:" + formattedDateTime);
// 解析日期时间
String dateTimeString = "2023-07-03 12:30:45";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeString, dateTimeFormatter);
System.out.println("解析后的日期时间:" + parsedDateTime);
}
}).start();
}
}
通过创建3个线程分别进行格式化时间和解析日期的操作,使用DateTimeFormatter能够安全的进行相关操作,没有什么问题
格式化后的日期时间:2024-08-03 08:48:06
格式化后的日期时间:2024-08-03 08:48:06
格式化后的日期时间:2024-08-03 08:48:06
解析后的日期时间:2023-07-03T12:30:45
解析后的日期时间:2023-07-03T12:30:45
解析后的日期时间:2023-07-03T12:30:45
SimpleDateFormat多线程下安全性代码演示
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 格式化日期时间
Date now = new Date();
String formattedDateTime = sdf.format(now);
System.out.println("格式化后的日期时间:" + formattedDateTime);
// 解析日期时间
String dateTimeString = "2023-07-03 12:30:45";
try {
Date parsedDateTime = sdf.parse(dateTimeString);
System.out.println("解析后的日期时间:" + parsedDateTime);
} catch (ParseException e) {
e.printStackTrace();
}
}
}).start();
}
}
同样的创建3个线程,进行相同的格式化时间和解析日期的操作,使用SimpleDateFormat则会抛出异常
格式化后的日期时间:2024-08-03 08:50:11
格式化后的日期时间:2024-08-03 08:50:11
格式化后的日期时间:2024-08-03 08:50:11
解析后的日期时间:Sat Jul 03 12:30:45 CST 2202
解析后的日期时间:Thu Jul 01 21:30:45 CST 2202
Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: ""
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.base/java.lang.Long.parseLong(Long.java:702)
at java.base/java.lang.Long.parseLong(Long.java:817)
at java.base/java.text.DigitList.getLong(DigitList.java:195)
at java.base/java.text.DecimalFormat.parse(DecimalFormat.java:2121)
at java.base/java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1933)
at java.base/java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1541)
at java.base/java.text.DateFormat.parse(DateFormat.java:393)
at org.example.randomTest$2.run(randomTest.java:81)
at java.base/java.lang.Thread.run(Thread.java:834)
DateTimeFormatter多线程安全原因
由此可见DateTimeFormatter是线程安全性,究其原因主要是DateTimeFormatter源码中入参是final 修饰的,不可变变量是线程安全的
SimpleDateFormat多线程下如果保证安全性?
当然如果多线程中一定要使用SimpleDateFormat也不是不行,只需要使用ThreadLocal
为每个线程提供独立的SimpleDateFormat实例,或者通过其他同步机制来保证线程安全。
SimpleDateFormat保证多线程安全代码演示
public class DateFormatTest {
private static final ThreadLocal<SimpleDateFormat> sdf =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static void main(String[] args) {
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 格式化日期时间
Date now = new Date();
String formattedDateTime = sdf.get().format(now);
System.out.println("格式化后的日期时间:" + formattedDateTime);
// 解析日期时间
String dateTimeString = "2023-07-03 12:30:45";
try {
Date parsedDateTime = sdf.get().parse(dateTimeString);
System.out.println("解析后的日期时间:" + parsedDateTime);
} catch (ParseException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
这样的话,多线程就没啥问题了
格式化后的日期时间:2024-08-03 09:05:50
格式化后的日期时间:2024-08-03 09:05:50
格式化后的日期时间:2024-08-03 09:05:50
格式化后的日期时间:2024-08-03 09:05:50
格式化后的日期时间:2024-08-03 09:05:50
解析后的日期时间:Mon Jul 03 12:30:45 CST 2023
解析后的日期时间:Mon Jul 03 12:30:45 CST 2023
解析后的日期时间:Mon Jul 03 12:30:45 CST 2023
解析后的日期时间:Mon Jul 03 12:30:45 CST 2023
解析后的日期时间:Mon Jul 03 12:30:45 CST 2023
设计哲学
SimpleDateFormat 的设计受到 Java 早期版本的限制,其 API 并不完全符合 Java 的新特性和设计哲学。SimpleDateFormat 的扩展性和灵活性较差,难以支持新的日期时间格式。
DateTimeFormatter 是 Java 8 日期时间 API(JSR-310)的一部分,这个新的 API 设计得更加模块化、易于理解和使用。DateTimeFormatter 提供了丰富的功能,包括解析和格式化日期时间,以及自定义模式字符串等。
国际化
SimpleDateFormat 在处理国际化时存在一些问题,特别是与区域设置(Locale)相关的日期时间格式。它可能无法完全满足各种语言和文化的需求。
DateTimeFormatter 提供了更好的国际化支持,能够根据区域设置自动选择适当的日期时间格式。同时,它也支持自定义模式字符串,允许开发者根据需要进行灵活的配置。
性能
尽管性能不是替换 SimpleDateFormat 的主要原因,但 DateTimeFormatter 在某些情况下可能表现出更好的性能。这主要是因为 DateTimeFormatter 是不可变的,可以在编译时进行优化,并且避免了线程同步的开销。
易用性和清晰度
DateTimeFormatter 的 API 设计得更加清晰和易用,提供了更多的方法和选项来配置和定制日期时间的格式。这使得开发者能够更容易地理解和使用它,同时也减少了出错的可能性。
总结
综上所述,Java 8 引入 DateTimeFormatter 替代 SimpleDateFormat 是出于多种考虑和优势,包括线程安全性、设计哲学、国际化支持、性能以及易用性和清晰度等方面。这些改进使得 Java 的日期时间处理更加现代、灵活和强大。因此,在Java 8及以后版本中,推荐使DateTimeFormatter来代替SimpleDateFormat进行日期时间的格式化和解析。