调用静态java.text.DateFormat的方法不可取?

我收到一个查找错误错误 – 调用静态java.text.DateFormat的方法,我不知道下面做这些事情不好/明智的原因。

private static final Date TODAY = Calendar.getInstance().getTime(); private static final DateFormat yymmdd = new SimpleDateFormat("yyMMdd"); private String fileName = "file_" + yymmdd.format(TODAY); 

DateFormats不是线程安全的,这意味着它们保持状态的内部表示。 如果多个线程同时访问同一个实例,在静态环境中使用它们会产生一些非常奇怪的错误。

我的build议是将你的variables局部放在你使用它们的地方,而不是使它们成为类的静态属性。 看起来你可能会在初始化类的时候这样做,所以你可以在构造函数中做到这一点:

 public class MyClass { private String fileName; public MyClass() { final Date today = Calendar.getInstance().getTime(); final DateFormat yymmdd = new SimpleDateFormat("yyMMdd"); this.fileName = "file_" + yymmdd.format(TODAY); } ... } 

如果您需要在多个地方使用格式化程序,则可以将模式设置为static final并在需要时创build新的本地DateFormat

 public class MyClass { private static final String FILENAME_DATE_PATTERN = "yyMMdd"; public void myMethod() { final DateFormat format = new SimpleDateFormat(FILENAME_DATE_PATTERN); // do some formatting } } 

该问题的FindBugs文档说:

正如JavaDoc所述,DateFormats对于multithreading使用本质上是不安全的。 检测器已经find了一个通过静态字段获得的DateFormat实例的调用。 这看起来很可疑。

欲了解更多信息,请参阅Sun Bug#6231579和Sun Bug#6178997。

而DateFormat的javadocbuild议:

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

杰克·莱(Jack Leow)的回答也有关于你今天静态使用“语义”的语义的一个好处。

顺便说一下,我已经看到这种情况发生在高stream量的生产环境中,开始debugging是一个非常混乱的事情; 所以在我的经验中,FindBugs警告实际上是一个有用的build议(不像其他一些静态分析规则,有时看起来很挑剔)。

Commons Lang有一个线程安全的FastDateFormat对象。 它只是格式化,而不是parsing。

如果你可以使用commons-lang这可能适合你。

 private static final Date TODAY = Calendar.getInstance().getTime(); private static final FastDateFormat yymmdd = FastDateFormat.getInstance("yyMMdd"); private String fileName = "file_" + yymmdd.format(TODAY); 

你确定没有

 private static final DateFormat yymmdd = new SimpleDateFormat("yyMMdd"); 

? 这就是错误信息所表明的。

我认为它的目标是DateFormat不是线程安全的,因此将一个实例作为一个静态字段来表示潜在的竞争条件。

我不确定FindBugs是否在抱怨这个问题,但是我发现你的代码中有一个问题,就是你将TODAY定义为一个类级(静态),常量(最终)variables。 这expression了你希望TODAY永远不会改变的意图(我不相信是这种情况,因为java.util.Dates是可变的,但这是另一回事)。

想想应用程序运行多天会发生什么? TODAY (除非你更新)将引用应用程序开始的一天,而不是当前的date。 你确定这是你的意思吗?

这可能不是你的代码中的一个bug,但目的不明确,我相信这可能是FindBugs抱怨的。

没有提到的替代方法是使用ThreadLocal。 请参阅http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html获取更多信息+ 3个选项之间的性能比较:

  • 每次创build一个实例
  • 同步访问
  • 使用ThreadLocal

使用ThreadLocal的例子:

 private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyMMdd"); } }; 

用法:

 DATE_FORMAT.get().format( TODAY ) 

首先,它不是线程安全的。

我认为这是因为格式不是线程安全的?

(我没有看到findbugs抱怨什么,你能提供警告文本吗?)

您可以通过在同步块中包装所有对DateFormat的引用来消除这一点 – 只要确保所有调用都包装在同一个同步对象中!