【问题标题】:Is there a good thread safe Format caching library?有没有好的线程安全格式缓存库?
【发布时间】:2012-08-03 19:37:48
【问题描述】:

我想在我的 webapp 中进行一些格式化,使用 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...);

【问题讨论】:

  • 如果找不到的话,你可以写一个。 ;)
  • 你做了哪些研究? stackoverflow 不是推荐引擎/谷歌。

标签: java multithreading


【解决方案1】:

tl;博士

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

切勿使用 SimpleDateFormat。 仅将 java.time 包用于日期时间工作。

java.time

诸如DateFormat 和SimpleDateFormat 等麻烦的旧日期时间类现在已被现代java.time 类所取代。具体在这里,DateTimeFormatter 类。

不可变对象,线程安全

java.time 类被设计为Immutable Objects。这意味着不是修改对象中的任何内容,而是生成一个新的不同对象。原件完好无损。

这个和其他技术,使 java.time 类 thread-safe 被设计并记录在案。

DateTimeFormatter

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

您可以缓存该对象dateTimeFormatterFullQuébec,保留它。

ZoneId

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

以continent/region 的格式指定proper time zone name,例如America/Montreal、Africa/Casablanca 或Pacific/Auckland。切勿使用 3-4 个字母的缩写,例如 EST 或 IST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

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

然后随时使用它们,甚至跨线程。

ZonedDateTime zdt = ZonedDateTime.now( zoneMontréal ) ;
String output = zdt.format( dateTimeFormatterFullQuébec ) ;

dimanche 2018 年 3 月 4 日 à 18:36:32 东部标准时间

java.time 对象,例如 ZonedDateTime 和 Instant 也是不可变的和线程安全的,就像 ZoneId 和 DateTimeFormatter。您可以缓存所有这些,并跨线程使用它们。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

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

从哪里获得 java.time 类?

  • Java SE 8、Java SE 9 及更高版本
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实现。
    • Java 9 添加了一些次要功能和修复。
  • Java SE 6 和 Java SE 7
    • ThreeTen-Backport 中的大部分 java.time 功能都向后移植到 Java 6 和 7。
  • Android
    • 更高版本的 Android 捆绑包实现 java.time 类。
    • 对于早期的 Android (ThreeTenABP 项目采用 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…。

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。

【讨论】:

    【解决方案2】:

    Apache Commons Lang 有FastDateFormat,它以正确的方式(在我看来)解决了问题,首先是线程安全的:

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

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

    【讨论】:

    • 确实解决了线程和缓存部分,但看起来它不会处理非日期格式。
    【解决方案3】:

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

    同步

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

    【讨论】:

    • 对 - 我只是出于这个原因建议使用 ThreadLocal,但没有解释原因。我编辑了原始帖子以澄清这不是真正的问题。
    猜你喜欢
    • 1970-01-01
    • 2019-10-25
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 2012-07-11
    • 2020-12-30
    • 2019-02-10
    相关资源
    最近更新 更多