问题描述
我已经在常量文件中声明了SimpleDateFormat对象为静态字段,如下所示,
Constants.java
public static final SimpleDateFormat GENERAL_TZ_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
在我的类文件中,我的实现是这样的。
String fDate = getTextValue(empNo,"firstDate");
if (null != fDate && !fDate.isEmpty()) {
try {
Date date = (Date)(Constants.GENERAL_TZ_FORMATTER).parse(fDate);
issue.setDate(date.getTime());
} catch (ParseException e) {
logUtil.error(LOG,e+ "date : " + date);
}
}
错误:
Exception while importing data. package name.SecureException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
我的问题是,在某些情况下,这会抛出NumberFormatException
(非常罕见的情况),因此我一直想知道自己做了诊断,并且大多数人都在解释这种情况可能会发生由于SimpleDateFormat不是线程安全的。如果是这种情况,我不清楚此代码如何在不使用多个线程的情况下在多线程环境中运行,是否会使DateFormat.parse()的输入为空字符串?
Java's SimpleDateFormat is not thread-safe article
我已尝试解决此问题,但是要重现此问题确实非常困难,我想了解您对此的想法,这将有助于我找到更好的解决方案。非常感谢您的建议。 谢谢。
解决方法
好吧,正如您的帖子下方的评论已经提到的那样,您无论如何都不应该使用SimpleDateFormat
。
您可能偶然发现了SimpleDateFormat
麻烦的情况。但是,这不是唯一的原因。 This Stackoverflow post explains why太麻烦了。在同一篇文章中提到SimpleDateFormat
不是线程安全的原因之一。线程安全性是,当多个进程作用于格式化程序时,即利用格式化程序来格式化日期,并且不会由于干扰而发生不正确,不准确或不确定的结果。
Your link to the article on Callicoder很好地解释了为什么 SimpleDateFormat
引起麻烦。帖子中提到的异常与您一样:
java.lang.NumberFormatException: For input string: ""
简而言之,线程在使用格式化程序时会发生干扰,因为格式化程序未同步。这意味着SimpleDateFormat
类不会强制一个线程必须等到另一个线程完成其内部状态修改后再执行。使用的三个类是SimpleDateFormat
,DateFormat
和FieldPosition
。
Here's erroneous code in action。
使用java.time
您需要转到java.time
软件包中可用的较新的Java 8 Date and Time API。由于它们的不变性,它们绝对是线程安全的。在您的情况下,请使用java.time.format.DateTimeFormatter
:
public static final DateTimeFormatter GENERAL_TZ_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
ZonedDateTime zdt = ZonedDateTime.parse(fDate,GENERAL_TZ_FORMATTER);
Instant instant = zdt.toInstant();
// Your setDate should really accept an Instant:
issue.setDate(instant);
// If that's REALLY not possible,then you can convert it to an integer
// value equal to the number of milliseconds since 1 January 1970,midnight
//issue.setDate(instant.toEpochMilli());