Java 使DateFormat线程安全。使用什么,同步还是线程本地

Java 使DateFormat线程安全。使用什么,同步还是线程本地,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我想使下面的代码线程安全。实现这一目标的最佳方式是什么 private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(); public static final String eventTypeToDateTimeString(long timestamp) { return DATE_FORMAT.format(new Date(timestamp)); } 最简单的解决办法是: synch

我想使下面的代码线程安全。实现这一目标的最佳方式是什么

private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance();

public static final String eventTypeToDateTimeString(long timestamp)
{
   return DATE_FORMAT.format(new Date(timestamp));
}

最简单的解决办法是:

synchronized public static final String eventTypeToDateTimeString(long timestamp) {
  return DATE_FORMAT.format(new Date(timestamp));
}

无需使用
ThreadLocal
s,因为这都是在静态上下文中进行的。

只需在每次调用时创建一个新副本,直到实际证明它是一个性能问题。手动管理线程局部变量的开销很可能会淹没您从缓存它们中获得的任何优势。

有几种备选方案,有不同的权衡

您可以同步对单个日期格式的访问。这可以最大限度地减少创建的格式化程序对象的数量,但是不同的线程在访问格式化程序之前必须争用一个锁。这可能是性能方面最差的替代方案;很多线程最终可能会花费时间等待,而且线程越多,情况就越糟

您可以为每次使用创建一个新的DateFormat对象。这将消除线程之间的争用,但是如果存在大量的日期格式,那么使用这种方法可能会给垃圾收集器带来压力,这将影响性能。但这在很多情况下都可以很好地工作,而且非常简单

第三种选择,将DateFormat设置为threadlocal变量,效率要高得多。线程之间没有争用,格式化程序可以被线程重复使用,因此它不会产生太多垃圾。缺点是,这是最不直接的方法,如果不清除,放入threadLocal中的任何对象可能会停留得比您想要的时间更长。

可以

  • 每次需要时创建一个新的
    DateFormat
    实例

  • 使用@Giovanni Botta指出的
    同步的

  • 使用
    ThreadLocal

    private static final ThreadLocal<DateFormat> THREADLOCAL_FORMAT =
        new ThreadLocal<DateFormat>() {
            @Override protected DateFormat initialValue() {
                return DateFormat.getDateTimeInstance();
            }
        };
    
    public static final String eventTypeToDateTimeString(long timestamp) {
        return THREADLOCAL_FORMAT.get().format(new Date(timestamp));
    }
    
    private static final ThreadLocal ThreadLocal\u格式=
    新ThreadLocal(){
    @重写受保护的DateFormat初始值(){
    return DateFormat.getDateTimeInstance();
    }
    };
    公共静态最终字符串eventTypeToDateTimeString(长时间戳){
    返回THREADLOCAL_FORMAT.get().FORMAT(新日期(时间戳));
    }
    
  • 实际上,如果您有一个线程池(意味着线程被重用),那么使用
    ThreadLocal
    可能会给您带来最好的性能,而大多数web容器都是这样做的

    参考文献

    避免使用传统的日期时间类 与最早版本的Java捆绑在一起的旧日期时间类已经被Java.time类所取代。time类是和使用的

    java.time 将格式化程序和日期类型替换为java.time类型以自动获得线程安全性

    如果需要,请在全局范围内定义。该类可以自动本地化正在生成的字符串,也可以指定某种格式

    • 指定一个用于确定缩写的长度
    • 指定a以确定(a)用于翻译日期名称、月份名称等的人类语言,以及(b)决定缩写、大写、标点符号等问题的文化规范
    • 为要调整时刻的时区指定一个
      ZoneId
    该类表示时间线上的一个时刻,分辨率为。该类将
    即时
    调整为特定时区

    您的代码,翻译成java.time类。在实际工作中,我会将其分成多行,并捕获异常

    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类型,例如。使用java.time类型提供了类型安全性,并使代码更加自文档化


    关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&

    该项目现已启动,建议迁移到类

    要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是

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

    从哪里获得java.time类

    • 、和更高版本-标准Java API的一部分,带有捆绑实现。
      • Java9添加了一些次要功能和修复
      • 大部分java.time功能都在中向后移植到Java6和Java7
      • 更高版本的Android捆绑包实现了java.time类

      • 对于早期的Android(更好,请使用:org.apache.commons.lang.time.FastDateFormat(SimpleDataFormat的快速和线程安全版本)

        每种类型的同步都可能会影响性能。但您可以尝试不同的解决方案,并比较对系统的影响。没有灵丹妙药。@正如OP所问的同步,所以我刚才提到了通过在同一实例上同步来重用同一实例的最简单解决方案。这一尝试的答案显示了一种可能性在最基本和最基本的层面上理解线程安全性和并发性是不可能的。不要做这个“答案”建议。旧类一团糟,但新类也一样。它们没有正确实现ISO8601,尽管它们提供了一些不同的半实现,不同重叠类之间的差距和行为各不相同。@DanielWinterstein我不理解你关于ISO8601没有正确实现的断言我建议你提供一些细节,也许可以发布你自己的问题和答案来解决这些问题。如果你指的是被吞没,我会