制作DateFormat Threadsafe。 使用什么,同步或线程本地
我想让以下代码线程安全。 实现它的最佳方法是什么?
private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(); public static final String eventTypeToDateTimeString(long timestamp) { return DATE_FORMAT.format(new Date(timestamp)); }
避免使用旧版日期时间类
与最早版本的Java捆绑在一起的麻烦的旧日期时间类已被java.time类取代。 java.time类是线程安全的并且使用不可变对象 。
java.time
用java.time类型替换格式化程序和日期类型以自动获得线程安全性。
如果需要,全局定义DateTimeFormatter
。 该类可以自动本地化生成的字符串,也可以指定某种格式。
- 指定
FormatStyle
以确定缩写的长度。 - 指定
Locale
以确定(a)用于翻译日期名称,月份名称等的人类语言,以及(b)决定缩写,大小写,标点符号等问题的文化规范。 -
ZoneId
区指定ZoneId
以调整时刻。
Instant
类表示UTC时间轴上的一个时刻,分辨率为纳秒 。 ZonedDateTime
类将Instant
调整为特定时区。
您的代码,转换为java.time类。 在实际工作中,我会将其分解为多行,并将陷入exception。
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH ) ; private static final ZoneId ZONE_ID = ZoneId.of( "America/Montreal" ); public static final String eventTypeToDateTimeString(long timestamp) { return Instant.ofEpochMilli( timestamp ).atZone( ZONE_ID ).format( DATE_TIME_FORMATTER ); }
不要将日期时间跟踪为从纪元开始计算
作为表示日期时间值的一种方式,我不建议传递很long
。 由于人类无法辨别日期时间值的含义,因此调试和记录困难。 相反,传递一下java.time类型,例如Instant
。 使用java.time类型可提供类型安全性,并使您的代码更加自我记录。
关于java.time
java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧遗留日期时间类,如java.util.Date
, Calendar
和SimpleDateFormat
。
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。
您可以直接与数据库交换java.time对象。 使用符合JDBC 4.2或更高版本的JDBC驱动程序 。 不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
- Java SE 8 , Java SE 9 , Java SE 10 , Java SE 11及更高版本 – 带有捆绑实现的标准Java API的一部分。
- Java 9增加了一些小function和修复。
- Java SE 6和Java SE 7
- 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
- Android的
- 更高版本的Android捆绑java.time类的实现。
- 对于早期的Android(<26), ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….
ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如Interval
, YearWeek
, YearQuarter
等。
有几种选择,有不同的权衡。
您可以同步对单个DateFormat的访问。 这样可以最大限度地减少创建的格式化程序对象的数量,但不同的线程在访问格式化程序之前必须争用锁定。 这可能是性能最差的替代方案; 很multithreading最终都会花费时间等待,而且你所拥有的线程越多,这就越糟糕。
您可以为每次使用创建一个新的DateFormat对象。 这将消除线程之间的争用,但如果有很多日期格式化,你可以用这种方法对垃圾收集器施加压力,这将损害性能。 但是在很多情况下这可以很好地工作并且非常简单。
第三种方法是使DateFormat成为一个threadlocal变量,效率要高得多。 线程之间没有争用,并且格式化程序可以被线程重复使用,因此它不会产生太多垃圾。 缺点是它是最简单的方法,如果你不清除它们,你放在threadLocal中的任何对象可能会比你想要的更长。
只需在每次调用时创建一个新副本,直到它实际certificate是性能问题。 手动管理线程本地的开销可能会淹没您从缓存中获得的任何优势。
您可以
-
每次需要时都创建一个新的
DateFormat
实例。 -
使用@Giovanni Botta指出的
synchronized
块。 -
使用
ThreadLocal
:private static final ThreadLocal
THREADLOCAL_FORMAT = new ThreadLocal () { @Override protected DateFormat initialValue() { return DateFormat.getDateTimeInstance(); } }; public static final String eventTypeToDateTimeString(long timestamp) { return THREADLOCAL_FORMAT.get().format(new Date(timestamp)); }
实际上,如果你有一个线程池(意味着线程被重用),使用ThreadLocal
可能会给你最好的性能,这是大多数Web容器所做的。
参考
http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html
更好,使用:org.apache.commons.lang.time.FastDateFormat(是SimpleDateFormat的快速且线程安全的版本)
最简单的解决方案是:
synchronized public static final String eventTypeToDateTimeString(long timestamp) { return DATE_FORMAT.format(new Date(timestamp)); }
不需要使用ThreadLocal
因为这都是在静态上下文中。