我正在对我们的日期格式化代码进行一些重构,因为由于各种原因,我们已经成功地引入了许多不一致的地方。我知道最好有一个ThreadLocal SimpleDateFormat。在这里讨论之后,我们不确定在Enum中使用时是否需要ThreadLocal,因为我们从不更改实例,也不公开它,这样它就不会发生变异?如果它仍然是必需的,做一个Enum,像这样,会破坏什么吗?还有什么东西坏了还是没做我认为应该做的事?基本上,我不需要太多的工作与ThreadLocal,我不确定的含意,特别是在它如何与Enum互动。
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 );
}
};
}
}发布于 2014-11-21 16:56:19
这里,枚举是一个全局常量,它被用作从线程局部变量中检索正确格式化程序的方便方法。枚举是不可变的,但是如果它引用了具有可变状态的事物,那么这些事情仍然会有问题。
当你说
我们从不更改实例,也不公开它,这样它就不会发生变异
您误解了线程安全问题的本质。问题是SimpleDateFormat的内部状态不受多个线程访问的保护,因此当多个线程访问同一个格式化程序实例时,任何这些线程都可以更改其他并发线程操作的状态,从而破坏结果。
处理此格式化程序的选择如下:
保留现有的ThreadLocal或签出线程安全格式化程序的替代方案可能是这里最好的选择。保留你所拥有的似乎是个安全的选择。
没有线程池,Threadlocal就变得不那么吸引人了,因为线程的生存期更短,所以对给定格式化程序的重用就更少了。线程池是一个好主意,因为有多种原因(主要是确保错误条件不会导致应用程序自身运行到线程中,以便您的应用程序可以以一种受控的方式降级),如果您没有使用它们,您应该这样做。
对我来说,这段代码最奇怪的地方是枚举必须是可序列化的,但是threadLocal不是可序列化的,所以必须声明为瞬态的。如果由于某种原因,这个东西确实被序列化并因此被反序列化,那么反序列化的副本将有一个空的threadLocal。实际上,这并不是您想要序列化的东西(您不会将其存储在HttpSession中,也不会将它传递给另一个JVM),因此这似乎是一个小到不存在的风险。
发布于 2014-11-21 16:32:19
您当前的实现很好,不应该通过删除ThreadLocal来更改它。SimpleDateFormat并不是线程安全的。换句话说,在没有外部同步的情况下跨线程使用相同的实例将不具有您预期的行为。javadoc警告您这一点。
ThreadLocal实例通过为每个线程提供一个实例来工作。此实例是不共享的(除非您共享它),因此不会造成任何问题。
它在enum中的事实并没有什么不同。
https://stackoverflow.com/questions/27065455
复制相似问题