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

Is java.util.Calendar thread safe or not?

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

因此,我做了一些研究,却一无所获。 有很多人认为它是线程安全的,还有很多人认为它不是线程安全的。 而且,最重要的是,文档没有以一种或另一种方式说任何东西,既不是针对Calendar,也不是针对Date

那是什么呢?


这是Java 7中Calendar和GregorianCalendar的源代码链接。

如果阅读该代码,您将看到没有实例方法被同步,并且实例字段都不是volatile。您还将看到,即使字段get方法也可能导致Calendar实例发生变异。并且由于没有执行同步,因此不同的线程可能会在执行此类更改操作后看到Calendar对象字段的旧版本。

作为记录,在调用此方法的过程中/期间,发生了get方法中的突变动作:

1
2
3
4
5
6
7
8
9
 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也不是因为它继承了非线程安全的字段和方法。

但是,不要只相信我的话。对源代码进行自己的分析。

And, to top it off, the documentation doesn't say anything one way or another, not for Calendar, nor even for Date.

如果javadocs没有指定类的线程安全,则应假定它不是线程安全的。


Oracle文档中没有任何有关线程安全的内容:http://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html。

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

1
2
3
4
5
6
7
8
public void setTimeInMillis(long millis) {
  // skipped
  time = millis;
  isTimeSet = true;
  areFieldsSet = false;
  computeFields();
  areAllFieldsSet = areFieldsSet = true;
}

我认为可以肯定该类不是线程安全的。