代码之家  ›  专栏  ›  技术社区  ›  setzamora

不建议调用静态java.text.dateformat方法?

  •  40
  • setzamora  · 技术社区  · 14 年前

    我收到一个 查找Bug 错误- 调用静态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);
    
    8 回复  |  直到 9 年前
        1
  •  76
  •   Community Neeleshkumar S    7 年前

    日期格式不是线程安全的,这意味着它们维护状态的内部表示。如果多个线程同时访问同一个实例,在静态上下文中使用它们可能会产生一些非常奇怪的错误。

    我的建议是将变量设置为使用它们的地方的局部变量,而不是将它们设置为类的静态属性。在初始化类时,您可能正在执行此操作,因此可以在构造函数中执行此操作:

    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所说,日期格式是 多线程固有的不安全性 使用。探测器发现有人打电话给 具有 通过静电场获得。这个 看起来可疑。

    有关更多信息,请参阅Sun。 Bug 6231579和Sun Bug 6178997。

    以及 javadoc for DateFormat 建议:

    日期格式不同步。它 建议单独创建 为每个线程设置实例格式。如果 多线程访问一种格式 同时,必须同步 外部。

    Jack Leow's answer 对于静态使用“today”的语义也有一个很好的观点。

    顺便说一句,我实际上已经看到这种情况发生在高流量的生产环境中,而且一开始调试是一件非常令人困惑的事情;因此,根据我的经验,findbugs警告实际上是一个有用的建议(与其他一些静态分析规则不同,这些规则有时看起来很挑剔)。

        2
  •  14
  •   ScArcher2    14 年前

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

    如果你能使用通用语言,这可能对你很有用。

    private static final Date TODAY = Calendar.getInstance().getTime();
    private static final FastDateFormat yymmdd = FastDateFormat.getInstance("yyMMdd");
    
    private String fileName = "file_" + yymmdd.format(TODAY);
    
        3
  •  4
  •   Michael Borgwardt    14 年前

    你确定不是吗

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

    ?这就是错误消息所指示的。

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

        4
  •  4
  •   Jack Leow    14 年前

    我不确定findbugs是否在抱怨这一点,但我发现您的代码存在一个问题,那就是您正在定义 TODAY 作为类级(静态)、常量(最终)变量。这传达了你想要的意图 今天 永不改变(我不相信是这样,因为java.util.dates是可变的,但那是另一个故事)。

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

    这可能根本不是代码中的错误,但目的并不明确,我相信 可以 成为findbugs抱怨的对象。

        5
  •  2
  •   Wim Deblauwe    9 年前

    另一个没有提到的方法是使用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 )
    
        6
  •  0
  •   Pointy    14 年前

    一方面,它不安全。

        7
  •  0
  •   Bjorn J    14 年前

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

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

        8
  •  0
  •   Paul Tomblin    14 年前

    通过将对日期格式的所有引用包装在同步块中,可以消除此问题-只需确保所有调用都包装在 相同的 同步对象!