有没有一个好的线程安全的格式缓存库?

4

我想在我的Web应用程序中进行一些格式化,使用MessageFormat、DateFormat、DecimalFormat等。

由于它们不是线程安全的,每个使用的静态实例都不起作用,但是每次需要一个新的XXXXFormat对象似乎是浪费的。使用ThreadLocal进行缓存和重用似乎是一个明显的优化。

这似乎是一个非常常见的模式,所以我想知道是否有适当的库。

而不是调用:

    DecimalFormat formatter = new DecimalFormat("###,##0.00");

    String formatted = formatter.format(value);             

每次我需要格式化某些内容时,为什么不:
    String formatted = FormatCache.formatDecimal("###,##0.00",numberValue);

在哪里,FormatCache将使用以格式模式为键的HashMap进行ThreadLocal缓存?

可能会有其他方法,例如:

    String FormatCache.formatDecimal(String, Number);
    String FormatCache.formatDate(String, Date);
    String FormatCache.formatMessage(String, Object...);

2
如果找不到,听起来你可以自己写一个。 ;) - Peter Lawrey
你做了哪些研究?stackoverflow不是推荐引擎或谷歌。 - Colin D
3个回答

5

简而言之

缓存一个线程安全的 DateTimeFormatter 对象(不可变)。

永远不要使用 SimpleDateFormat 仅使用 java.time 包进行日期时间处理。

java.time

麻烦的旧日期时间类,如DateFormatSimpleDateFormat现在被现代的java.time类所取代。特别是这里的DateTimeFormatter类。

不可变对象,线程安全

java.time类被设计为不可变对象。这意味着,而不是修改对象中的任何内容,会生成一个新的独立对象。原始对象保持不变。

通过此方法和其他技术,java.time类被设计为线程安全,并有文档记录。

DateTimeFormatter

DateTimeFormatter dateTimeFormatterFullQuébec = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH ) ;

您可以缓存该对象dateTimeFormatterFullQuébec,并将其保留。

ZoneId

同样,您可以缓存ZoneId时区对象。

continent/region的格式指定适当的时区名称,例如America/MontrealAfrica/CasablancaPacific/Auckland。永远不要使用3-4个字母的缩写,如ESTIST,因为它们不是真正的时区,不标准化,甚至不唯一(!)。

ZoneId zoneMontréal = ZoneId.of( "America/Montreal" ) ;

然后随时使用它们,甚至跨线程。
ZonedDateTime zdt = ZonedDateTime.now( zoneMontréal ) ;
String output = zdt.format( dateTimeFormatterFullQuébec ) ;

2018年3月4日,美国东部标准时间下午6:36:32

java.time对象(如ZonedDateTimeInstant)也与ZoneIdDateTimeFormatter一样是不可变的和线程安全的。您可以将它们缓存起来,并在多个线程中使用。


关于 java.time

java.time 框架内置于 Java 8 及更高版本。这些类替代了老旧的 遗留 日期时间类,如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在处于 维护模式,建议迁移到 java.time 类。

要了解更多信息,请参阅 Oracle 教程。并搜索 Stack Overflow 获取许多示例和解释。规范为 JSR 310

您可以直接与数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。不需要字符串,也不需要 java.sql.* 类。

如何获取 java.time 类?

ThreeTen-Extra项目通过添加额外的类扩展了java.time。该项目是java.time可能未来增加内容的试验场。您可能会在这里找到一些有用的类,例如Interval, YearWeek, YearQuarter,以及更多


5

Apache Commons Lang提供了FastDateFormat,这个类解决了问题(在我看来)的正确方式,因为它本身就是线程安全的:

FastDateFormat是SimpleDateFormat的快速和线程安全版本。

在大多数格式化情况下,可以直接使用此类作为SimpleDateFormat的替代品。在多线程服务器环境中,此类特别有用。无论在任何JDK版本中,SimpleDateFormat都不是线程安全的,也不会成为线程安全,因为Sun已经关闭了该bug/RFE。


肯定涉及线程和缓存部分,但似乎无法处理非日期格式。 - Brad Tofel

2

在这方面你应该非常小心。标准(简单)格式化程序不是线程安全的。我曾经遇到过一些多线程相关的问题,其中涉及了一些共享/缓存的格式化程序,但那是几年前的事情(Java 1.4)。如果你查看JavaDocs(SimpleDateFormat),你会注意到以下内容:

同步

日期格式不是同步的。建议为每个线程创建单独的格式实例。如果多个线程同时访问一个格式,则必须在外部进行同步。


1
没错 - 我之前建议使用ThreadLocal,但没有解释原因。我已经编辑了原始帖子以澄清这不是真正的问题。 - Brad Tofel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接