为什么我的Java Calendar.setTime()偶尔设置错误时间?
使用下面的代码,我注意到有时日期格式不正确。为了让它变得更奇怪,有时timeStamp会有正确的日期,timeStampCopy会有错误的日期,反之亦然。为什么我的Java Calendar.setTime()偶尔设置错误时间?
public static Timestamp method(String date, DateFormat dateFormat) throws Exception {
// date is always "2017-02-17"
// original
GregorianCalendar gCal = new GregorianCalendar();
gCal.setTime(dateFormat.parse(date));
Timestamp timeStamp = new Timestamp(gCal.getTimeInMillis());
// copy
GregorianCalendar gCalCopy= new GregorianCalendar();
gCalCopy.setTime(dateFormat.parse(date));
Timestamp timeStampCopy = new Timestamp(gCalCopy.getTimeInMillis());
if (!timeStamp.toString().contains("2017-02-17"))
System.out.println(timeStamp.toString());
if (!timeStampCopy.toString().contains("2017-02-17"))
System.out.println(timeStampCopy.toString());
return timeStamp;
}
我不知道这可能是导致它,但我这个使用Date对象和我有同样的问题尝试。我认为这可能是一个解析问题,但由于它做了两次相同的事情,我不确定。
下面是一些我得到的值:
timeStamp is: 2017-02-17 00:00:00.0
timeStampCopy is: 1700-02-17 00:00:00.0
你说你共享线程之间的DateFormat
实例。
按照Javadoc:
日期格式不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一个格式,它必须在外部同步。
注意,这指的是访问DateFormat
实例的外部同步,不的方法。如果DateFormat
实例没有其他用途,则仅使用方法将修复此问题。
您可以:
- 显式同步,周围用
DateFormat
实例的所有代码(这是值得添加@GuardedBy
注释的变量,以记录您所期望的锁被使用前举行); - 将变量类型更改为
ThreadLocal<DateFormat>
(并适当地初始化共享变量),以确保每个线程都拥有自己的DateFormat
副本。
后一种方法争用较少,因为每个线程都可以独立于其他线程进行。这也意味着您不会意外省略同步。
但是,还有更好的库处理日期和时间,这是设计与DateFormat
缺乏线程安全问题的后见之明。在Java 8中,有java.time
API;对于早期版本的Java,有Jodatime。
所有的都不错,但是最后一句话。 Joda-Time项目现在处于维护模式,并建议迁移到java.time类。对于早期版本的Java(6和7),请使用[ThreeTen-Backport](http://www.threeten.org/threetenbp/)项目。大部分java.time功能都在那里被移植。在[ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP)项目中进一步适用于Android。 –
Answer by Turner是正确的,应该被接受。
java.time是线程安全的
的java.time类使用不可变对象,使他们本质上就是线程安全的解决这个问题。
LocalDate ld = LocalDate.of("2017-02-17");
ZoneId z = ZoneId.of("America/Montreal");
ZonedDateTime zdt = ld.atStartOfDay(z);
通过调用toString
生成标准ISO 8601格式的字符串。对于其他格式,请使用DateTimeFormatter
类。搜索堆栈溢出了许多示例和讨论。不用担心线程,所有线程安全。
对于UTC值,请提取Instant
。
Instant instant = zdt.toInstant() ;
不需要使用java.sql.Timestamp
。现代JDBC驱动程序可以通过toObject和setObject方法处理java.time类型。对于较旧的驱动程序,使用添加到旧类的新方法进行转换。
关于java.time
的java.time框架是建立在Java 8和更高版本。这些类取代了日期时间类legacy,如java.util.Date
,Calendar
,& SimpleDateFormat
。
Joda-Time项目现在位于maintenance mode,建议迁移到java.time类。请参阅Oracle Tutorial。并搜索堆栈溢出了很多例子和解释。规格是JSR 310。
从何处获取java.time类?
-
Java SE 8和SE 9后来
- 内置。
- 带捆绑实现的标准Java API的一部分。
- Java 9增加了一些次要功能和修复。在ThreeTen-Backport
-
Java SE 6和SE 7
- 大部分的java.time功能后移植到Java 6 & 7。
-
Android
- 的ThreeTenABP项目适应ThreeTen-反向移植(上述)为Android特异性。
- 参见How to use ThreeTenABP…。
的ThreeTen-Extra项目与其他类扩展java.time。这个项目是未来可能增加java.time的一个试验场。您可以在这里找到一些有用的类,如Interval
,YearWeek
,YearQuarter
和more。
什么是你的功能输入 –
你是否在线程之间共享'DateFormat'实例? –
@AndyTurner是的!我刚刚补充说,由于某种原因,当我向该方法添加“synchronized”时,它似乎突然开始正常工作。不知道为什么。 – Overclock