Calendar, DateFormat and multi threading
Another anecdote from the ongoing Java 1.4 to Java 5 transition... Last time it was an incarnation of "Favor composition over inheritance". This time I am writing about a multi-threading issue which can be equally subtle and difficult to pinpoint.
On the test systems we have set up we observed two strange types of exceptions at seemingly random times and in various different areas of the application. They looked similar to the following two patterns:
java.lang.ArrayIndexOutOfBoundsException: 432 at sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:436) at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2024) at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:1941) at java.util.Calendar.setTimeInMillis(Calendar.java:1066) at java.util.Calendar.setTime(Calendar.java:1032) ... (our classes)
java.lang.ClassCastException: java.util.SimpleTimeZone at java.util.Calendar.writeObject(Calendar.java:1643) at sun.reflect.GeneratedMethodAccessor500.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:324) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:795) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1294) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1245) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1052) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1330) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1302) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1245) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1052) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1330) ... (more of this) ... (our classes)
Those errors, especially the first one, would suddenly happen at a seemingly random place. Re-running the program with the same inputs on the same machine would sometimes complete normally and sometimes crash at a different place.
Searching for the
sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:436) line brought me to Java Bug #6231579 which describes exactly our problem. The bug has been closed and classified as "not a bug", because they say that
Calendar was not designed to be thread-safe and that suchlike behavior can be expected when sharing an instance over several threads. This is exactly what we found in our product: In a utility class we used two
static final java.util.Calendar instances that were shared among all threads using the utility. Interestingly
Calendar's javadoc does not mention the unsafe nature of the
Calendar class. Alright, thinking about it in retrospective sharing them was not such a good idea after all, because one could have guessed that there might be problems. So changing this piece of code was rather easy, once it had been found.
SimpleDateFormat's documentation includes a hint about synchronization issues. This one is even more important, because there are probably many more people using some shared instances of those and get bitten. There is a series of bugs reported about this, one of them being Bug #4264153.
So maybe it is time to search your code for all
static usages of the
Calendar and various
...Format classes, before you start getting strange errors.