2010-03-09 75 views
37

我收到一个发现错误错误 - 调用静态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); 
+1

除了调用方法,这段代码确实看起来有点怀疑 - “TODAY”将是一个不变的日子,格式化程序是最终的非静态的? – Pool 2010-03-09 14:30:51

+2

今天也不总是“今天”,如果例如这个类被加载,然后JVM被运行到第二天 - 任何依赖TODAY成为当天的逻辑将不起作用,除非你是占这种差异。 – Peter 2010-03-09 15:17:24

+0

@彼得 - 是的,程序正在为每次运行重置。 – Joset 2010-03-09 15:19:35

回答

68

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

我的建议是将你的变量局部放在你使用它们的地方,而不是使它们成为类的静态属性。它看起来就像你可能会做这个,当你初始化类,所以你可以做到这一点在构造函数:

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并在需要时创建新的本地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 documentation的问题说:

正如Javadoc,是的DateFormats 固有不安全的多线程使用 。检测器发现 的一个调用DateFormat的实例,该实例已通过静态字段获得 。这 看起来可疑。

欲了解更多信息,请参阅太阳 错误#6231579和太阳错误#6178997。

而且javadoc for DateFormat提示:

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

Jack Leow's answer对于“TODAY”的静态使用的语义也有一个好的观点。另外,我已经看到这种情况发生在高流量的生产环境中,首先要进行调试是一件非常令人困惑的事情;所以根据我的经验,FindBugs警告实际上是一个有用的建议(与其他一些静态分析规则不同,有时似乎太挑剔)。

+1

我不太清楚,我认为每次调用myMethod()时创建一个SimpleDateFormat都太昂贵了。 – Joset 2010-03-09 15:31:38

+1

@eradicus - 您是否有性能指标证明它“太贵”?除非你已经注意到它是应用程序的瓶颈,否则我宁愿采用本地方法来处理全局范围的“static final”。强制Knuth:http:// c2。com/cgi/wiki?PrematureOptimization – 2010-03-09 15:53:56

+0

为什么变量不是“最终的”,即不变?如果它不能改变所有的线程应该能够分享它,不是吗?或者它是初始化竞争条件的问题? – kossmoboleat 2014-07-24 14:08:16

0

这不是线程安全的,一方面。

4

你确定这不是

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

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

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

+0

你是对的。有一个DateFormat的静态修饰符 – Joset 2010-03-09 14:30:30

0

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

(我还没有看到什么FindBugs的抱怨,你可以提供 警告文本?)

0

你能得到这个走开通过包装所有引用的同步块中的日期格式 - 只要确保所有的呼叫都包在同步对象!

+0

这可能会摆脱FindBugs错误,但是当你需要时新建一个本地DateFormat不是更合乎逻辑吗? – 2010-03-09 14:52:40

+0

@Rob,我想这将取决于同步是否实际上可能在一段时间内阻塞一次,或者如果它只是一个安抚FindBugs的形式。 – 2010-03-09 15:27:01

+0

是的。我想如果创建一个新的DateFormat的开销会影响系统性能,那么同步可能是一种选择,但我怀疑它在大多数情况下是否会发生。另外,如果是这样的话,它可能会是一个更高负载的系统,我认为同步会以类似的方式阻碍性能。 – 2010-03-09 15:31:06

4

我不确定FindBugs是否在抱怨这个问题,但我在代码中看到的一个问题是,您将TODAY定义为类级别(静态),常量(最终)变量。这意味着你想要TODAY永远不会改变(我不相信这是事实,因为java.util.Dates是可变的,但这是另一回事)。

想想如果您的应用程序运行多天会发生什么? TODAY(除非您更新它)将引用应用程序启动的那一天,而不是当前日期。你确定这是你的意思吗?

这可能不是您代码中的错误,但意图不明确,我相信可能是是FindBugs抱怨的。

+0

是的,今天一定不能改变。 – Joset 2010-03-09 15:11:32

12

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

如果您可以使用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); 
2

未提及的替代方法是使用ThreadLocal。见http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html为3个选项之间详细信息+性能对比:

  • 创建实例每次
  • 同步访问
  • 使用的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)