关于线程安全的困惑 – SimpleDateFormat示例

我有一个关于线程安全的问题。 据我所知,SimpleDateFormat不是线程安全的。 我想知道如果我在弹簧控制器中使用以下方式会有什么影响:

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.parse()使用名为calendar的实例变量来构建字符串中的日期。 如果两个线程同时尝试解析,则calendar变量将被破坏,您将得到错误的结果。

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

那么我会用这种方式看到什么样的问题呢?

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

只需删除static关键字就可以修复任何问题,因为每个线程都会使用自己的dateFormat实例吗?

删除static对您没有帮助,因为默认情况下Spring控制器是单一作用域,因此Spring使用控制器的单个实例来提供所有请求。

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

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

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

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

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

Android开发人员可以使用SimpleDateFormat中的安全(线程本地化)包装器: org.apache.http.impl.cookie.DateUtils

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

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