线程安全的困惑 - SimpleDateFormat示例

11

我对线程安全有一个问题。据我所知,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实例?在线程安全方面对此问题的任何澄清将不胜感激。

谢谢


2
这让我想起了我一段时间前提出的一个问题,虽然它并没有直接回答你的问题,但你可能会发现看到使用SimpleDateFormat的替代方式很有用。 https://dev59.com/fG855IYBdhLWcg3w3IRh - 3urdoch
谢谢提供链接。你最终采用了哪种技术? - blong824
选项3对我来说非常有效。 - 3urdoch
7个回答

22

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

将该变量设置为非静态可能并不能解决问题,因为两个线程仍然可以使用同一个控制器。更好的解决方案是每次解析日期时创建一个新的DateFormat对象,或者使用线程本地存储。更好的选择是使用JodaTime,它具有线程安全的解析器。


1
为什么SimpleDateFormat中的这个不被视作一个bug呢? - Damo
大多数开发人员都知道,对于大多数不是线程安全的类,这是由于同时更改状态。一旦格式确定,格式化日期就不应更改状态。仅在官方文档中记录此内容为非线程安全是不够的。如果格式方法在实例变量中维护临时状态,则应明确记录即使格式方法也不是线程安全的。将其声明为静态不仅是新手错误。可以将修改集合(put)与访问集合(get)进行类比。 - YoYo

5
那么,这样使用会遇到什么问题呢?SimpleDateFormat的开发人员做出了一个非常奇怪的决定-在parse()的工作过程中,他们将部分解析的日期存储在SimpleDateFormat的字段中。显然,这意味着您不能同时从几个线程调用parse()。
如果只是删除static关键字,是否会解决任何问题,因为每个线程都将使用自己的dateFormat实例?
删除static不会帮助您,因为Spring控制器默认为单例范围,因此Spring使用单个控制器实例来服务所有请求。

2
不确定如果您这样做会遇到什么问题。但是Javadocs警告不要同时访问SimpleDateFormat,而您使用的方式肯定涉及并发访问。除非您正在实现封闭类的某种同步策略或以其他方式防止多个线程访问该类,否则删除静态关键字将无法消除并发性问题。
您可以尝试通过在方法体内实例化每个线程的SimpleDateFormat并确保SimpleDateFormat的引用从未“逃逸”该方法来解决此问题。换句话说,在同一方法中声明变量、实例化对象并使用对象。这将确保当方法退出时,该SimpleDateFormat的引用将被删除。

2

个人而言,我会避免所有这些问题,直接使用JodaTime。该API更加丰富,没有线程问题,并且速度更快。


2

SimpleDateFormat具有解析过程中的实例状态,因此不是线程安全的。如果您从多个线程使用它,它将崩溃(就像Java崩溃一样:-),不会出现进程崩溃之类的情况)。删除静态关键字并不一定能解决问题,因为它取决于实例,仍可能从多个线程中使用。

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


0

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接