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

时间:2011-04-13 16:30:38

标签: java spring thread-safety

我对线程安全有疑问。据我所知,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实例吗?关于线程安全的任何关于线程安全的清晰度都将非常感激。

由于

7 个答案:

答案 0 :(得分:21)

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

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

答案 1 :(得分:5)

  

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

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

  

简单地删除static关键字会修复任何问题,因为每个线程都会使用自己的dateFormat实例吗?

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

答案 2 :(得分:2)

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

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

答案 3 :(得分:2)

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

答案 4 :(得分:2)

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

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

答案 5 :(得分:0)

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

此处实现的源代码(例如FROYO API Level 8):

答案 6 :(得分:0)

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