java.util.Calendar是否线程安全?

26

我一直以为DateCalendar都不是线程安全的,但在最近的讨论中,我的同事告诉我Calendar是线程安全的。

于是我进行了一些研究,但没有找到确切答案。有很多人认为它是线程安全的,也有很多人认为它不是线程安全的。更糟糕的是,文档也没有明确说明,无论是对于Calendar还是Date都是如此。

那么,它到底是线程安全的还是非线程安全的呢?


3
请查看此链接:https://dev59.com/JlfUa4cB1Zd3GeqPMu4X - Alex Coleman
@AlexColeman 注意,第一个答案说“不”,第二个答案说“是”,但所有这些讨论都没有任何支持。 - Daniel C. Sobral
1
+1 给 Joda Time,如果线程安全是你的顾虑,则使用它将是一个不错的选择。 - Sujay
冒昧地指出,每次调用Calendar.getInstance()都会返回一个新对象。因此,只要您在单个线程中使用实例,线程安全可能不是您需要考虑的问题。 - mojoken
1
@mojoken 冒昧地说,线程安全涉及到多个线程使用同一个实例。 - Daniel C. Sobral
显示剩余2条评论
2个回答

38

这里提供了Java 7中 CalendarGregorianCalendar 的源代码链接。

如果你阅读代码,会发现其中没有任何实例方法被同步,也没有任何实例字段被标记为volatile。而且甚至get方法的字段也可能导致Calendar实例的变化。由于没有执行同步操作,不同的线程在进行这样的变异操作后可能会看到过时版本的Calendar对象字段。

值得一提的是,在get方法中发生的突变动作发生在/期间调用此方法:

 1555 protected void complete()
 1556       {
 1557           if (!isTimeSet)
 1558               updateTime();
 1559           if (!areFieldsSet || !areAllFieldsSet) {
 1560               computeFields(); // fills in unset fields
 1561               areAllFieldsSet = areFieldsSet = true;
 1562           }
 1563       }

简而言之,Calendar类不是线程安全的,GregorianCalendar也不是因为它继承了不安全的字段和方法。

但不要仅凭我的话。请自己分析源代码。


更糟糕的是,文档对于 Calendar 甚至 Date 都没有说明它们是否线程安全。

如果 javadocs 没有明确说明一个类的线程安全性,则应该假设它不是线程安全的。(特别是如果这个类通过设计是可变的。)


4

Oracle的文档中没有提到线程安全:http://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html.

OpenJDK源代码(版本b147)以非线程安全的方式实现了java.util.Calendar,例如:

public void setTimeInMillis(long millis) {
  // skipped
  time = millis;
  isTimeSet = true;
  areFieldsSet = false;
  computeFields();
  areAllFieldsSet = areFieldsSet = true;
}

我认为可以安全地假设该类不是线程安全的。


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