【问题标题】:Confusion about thread safety - SimpleDateFormat example关于线程安全的困惑 - SimpleDateFormat 示例
【发布时间】:2011-04-13 16:30:37
【问题描述】:

我有一个关于线程安全的问题。据我所知,SimpleDateFormat 不是线程安全的。我想知道如果我在我的 spring 控制器中使用它会产生什么效果:

private final static SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd yyyy", Locale.US);

稍后在我的控制器功能中,我按如下方式使用它:

  try {
        changedate = changedate.substring(0, 15);                                                
        calcDate = dateFormat.parse(changedate);
    } catch (ParseException e2) {
        logger.error("Date Parsing Problem", e2); 
    }

calcDate 然后被添加到我的模型对象中并返回一个 ModelAndView。

那么这样使用会遇到什么样的问题呢?是否会简单地删除 static 关键字来解决任何问题,因为每个线程都将使用自己的 dateFormat 实例?任何关于线程安全的主题的澄清将不胜感激。

谢谢

【问题讨论】:

  • 这让我想起了我前段时间提出的一个问题,虽然它没有具体回答您的问题,但您可能会发现查看使用 SimpleDateFormat 的替代方法很有用。 stackoverflow.com/questions/4107839/…
  • 感谢您的链接。您最终实施了哪种技术?
  • 选项 3 对我很有效。

标签: java spring thread-safety


【解决方案1】:

SimpleDateFormat.parse() 使用名为calendar 的实例变量从字符串构建日期。如果两个线程试图同时解析,calendar 变量将被破坏,你会得到错误的结果。

使变量不是静态的不一定有帮助,因为两个线程仍然可以使用同一个控制器。更好的解决方案是在每次解析日期时创建一个新的 DateFormat 对象,或者使用线程本地存储。更好的是,使用具有线程安全解析器的JodaTime

【讨论】:

  • 为什么这不被视为 SimpleDateFormat 中的错误?
  • 大多数开发人员都明白,对于大多数非线程安全的类,这是由于同时更改状态所致。建立格式后,格式化日期不应更改状态。仅在官方文档中将其记录为不是线程安全的是不够的。应该明确记录,即使格式方法在实例变量中保持临时状态,也不是线程安全的。将其声明为静态不仅仅是一个新手错误。可以在修改集合 (put) 与访问集合 (get) 之间进行类比。
【解决方案2】:

那么这样使用会遇到什么样的问题呢?

SimpleDateFormat 的开发人员做出了一个非常奇怪的决定——在parse() 的工作期间,他们将部分解析的日期存储在SimpleDateFormat 的字段中。显然,这意味着你不能同时从多个线程调用parse()

是否会简单地删除 static 关键字来解决任何问题,因为这样每个线程都将使用自己的 dateFormat 实例?

删除 static 对您没有帮助,因为 Spring 控制器默认是单例范围的,因此 Spring 使用控制器的单个实例来处理所有请求。

【讨论】:

    【解决方案3】:

    就个人而言,我会通过使用 JodaTime 来避免所有这些问题。 API 更丰富,没有线程问题,而且速度更快。

    【讨论】:

      【解决方案4】:

      SimpleDateFormat 在解析时具有实例范围的状态,因此不是线程安全的。如果您从多个线程中使用它,它将崩溃(就像 java 崩溃 :-),没有进程崩溃等)。删除 static 关键字不一定能解决问题,因为它取决于实例,并且仍可能从多个线程中使用。

      您可以在上述方法中创建一个本地实例,以便每次解析都使用自己的格式化程序或使用线程局部变量。

      【讨论】:

        【解决方案5】:

        不确定如果您这样做会遇到什么类型的问题。但是 Javadocs 警告不要同时访问 SimpleDateFormat 以及使用它的方式肯定会涉及并发访问。除非您为封闭类实施某种类型的同步策略或以其他方式阻止多个线程访问该类,否则删除静态不会消除并发问题。

        您可以尝试为每个线程创建一个 SimpleDateFormat,方法是在方法体中对其进行实例化,并确保对 SimpleDateFormat 的引用永远不会“转义”该方法。换句话说,声明变量,实例化对象,并在同一个方法中使用对象。这将确保在方法退出时删除该 SimpleDateFormat 的引用。

        【讨论】:

          【解决方案6】:

          Android 开发者可以使用 SimpleDateFormat 的安全(线程本地化)包装器,它们位于:org.apache.http.impl.cookie.DateUtils

          实现的源代码在这里(例如 FROYO API Level 8):

          【讨论】:

            【解决方案7】:

            另一种方法是,如果您可以确保每次调用控制器时,您都返回一个新实例,然后删除静态引用。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-10-26
              • 2016-09-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-10-04
              • 2012-08-13
              • 2021-01-13
              相关资源
              最近更新 更多