我一直以为Date
和Calendar
都不是线程安全的,但在最近的讨论中,我的同事告诉我Calendar
是线程安全的。
于是我进行了一些研究,但没有找到确切答案。有很多人认为它是线程安全的,也有很多人认为它不是线程安全的。更糟糕的是,文档也没有明确说明,无论是对于Calendar
还是Date
都是如此。
那么,它到底是线程安全的还是非线程安全的呢?
这里提供了Java 7中 Calendar 和 GregorianCalendar 的源代码链接。
如果你阅读代码,会发现其中没有任何实例方法被同步,也没有任何实例字段被标记为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 没有明确说明一个类的线程安全性,则应该假设它不是线程安全的。(特别是如果这个类通过设计是可变的。)
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;
}
我认为可以安全地假设该类不是线程安全的。
Joda Time
,如果线程安全是你的顾虑,则使用它将是一个不错的选择。 - Sujay