首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ThreadLocal SimpleDateFormat in a Enum?

ThreadLocal SimpleDateFormat in a Enum?
EN

Stack Overflow用户
提问于 2014-11-21 16:01:33
回答 2查看 1.2K关注 0票数 2

我正在对我们的日期格式化代码进行一些重构,因为由于各种原因,我们已经成功地引入了许多不一致的地方。我知道最好有一个ThreadLocal SimpleDateFormat。在这里讨论之后,我们不确定在Enum中使用时是否需要ThreadLocal,因为我们从不更改实例,也不公开它,这样它就不会发生变异?如果它仍然是必需的,做一个Enum,像这样,会破坏什么吗?还有什么东西坏了还是没做我认为应该做的事?基本上,我不需要太多的工作与ThreadLocal,我不确定的含意,特别是在它如何与Enum互动。

代码语言:javascript
复制
public enum DateFormat {
DATE(newThreadLocalSimpleDateFormat( "MM/dd/yyyy" )),
LONG_DATE(newThreadLocalSimpleDateFormat("MMMM dd, yyyy")),
TIMESTAMP(newThreadLocalSimpleDateFormat( "MM/dd/yyyy hh:mm:ss aa" ));

private transient final ThreadLocal<SimpleDateFormat> formatter;

DateFormat( final ThreadLocal<SimpleDateFormat> formatter ) {
    this.formatter = formatter;
}


public  String format ( final Date date ) {
    return this.formatter.get().format( date );
}

private static ThreadLocal<SimpleDateFormat> newThreadLocalSimpleDateFormat( final String frmtString ) {
    return new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat( frmtString );
        }
    };
}

}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-11-21 16:56:19

这里,枚举是一个全局常量,它被用作从线程局部变量中检索正确格式化程序的方便方法。枚举是不可变的,但是如果它引用了具有可变状态的事物,那么这些事情仍然会有问题。

当你说

我们从不更改实例,也不公开它,这样它就不会发生变异

您误解了线程安全问题的本质。问题是SimpleDateFormat的内部状态不受多个线程访问的保护,因此当多个线程访问同一个格式化程序实例时,任何这些线程都可以更改其他并发线程操作的状态,从而破坏结果。

处理此格式化程序的选择如下:

  • 保持ThreadLocal完整,这样每个线程都可以获得自己的格式化程序副本;在某些情况下,最大的危险是线程本地对象可能无法被正确清理,因此从池中选择的线程(您使用的是线程池,对吗?)可能有一个与以前的使用相关联的变量,但在这种情况下,它可能更像是一个特性:如果所有线程都需要这样做,最好是格式化程序继续使用,
  • 为每个调用创建一个新的格式化程序,这样就不会共享任何东西(快速、简单和线程安全,但这会在格式化程序被丢弃时产生垃圾;让垃圾收集器更努力地工作将降低性能),
  • 同步对格式化程序的访问,以便线程不能同时访问它(可能是最不吸引人的选择,可能会造成瓶颈)
  • 使用DateFormat的不同实现,如FastDateFormat,即线程安全(线程安全可能意味着实现要么是锁定的,要么是复制状态,因此可能有缺点,需要进行一些测试才能看到结果)。

保留现有的ThreadLocal或签出线程安全格式化程序的替代方案可能是这里最好的选择。保留你所拥有的似乎是个安全的选择。

没有线程池,Threadlocal就变得不那么吸引人了,因为线程的生存期更短,所以对给定格式化程序的重用就更少了。线程池是一个好主意,因为有多种原因(主要是确保错误条件不会导致应用程序自身运行到线程中,以便您的应用程序可以以一种受控的方式降级),如果您没有使用它们,您应该这样做。

对我来说,这段代码最奇怪的地方是枚举必须是可序列化的,但是threadLocal不是可序列化的,所以必须声明为瞬态的。如果由于某种原因,这个东西确实被序列化并因此被反序列化,那么反序列化的副本将有一个空的threadLocal。实际上,这并不是您想要序列化的东西(您不会将其存储在HttpSession中,也不会将它传递给另一个JVM),因此这似乎是一个小到不存在的风险。

票数 2
EN

Stack Overflow用户

发布于 2014-11-21 16:32:19

您当前的实现很好,不应该通过删除ThreadLocal来更改它。SimpleDateFormat并不是线程安全的。换句话说,在没有外部同步的情况下跨线程使用相同的实例将不具有您预期的行为。javadoc警告您这一点。

ThreadLocal实例通过为每个线程提供一个实例来工作。此实例是不共享的(除非您共享它),因此不会造成任何问题。

它在enum中的事实并没有什么不同。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27065455

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档