United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-7017458 (cal) Multithreaded deserialization of Calendar leads to ClassCastException
JDK-7017458 : (cal) Multithreaded deserialization of Calendar leads to ClassCastException

Details
Type:
Bug
Submit Date:
2011-02-05
Status:
Resolved
Updated Date:
2013-09-19
Project Name:
JDK
Resolved Date:
2012-02-07
Component:
core-libs
OS:
windows_vista,solaris_10
Sub-Component:
java.util:i18n
CPU:
x86,sparc
Priority:
P4
Resolution:
Fixed
Affected Versions:
6u23,6u25
Fixed Versions:

Related Reports
Backport:
Backport:
Backport:
Backport:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version "1.6.0_23"
Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
Java HotSpot(TM) Client VM (build 19.0-b09, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [version 6.0.6002]

EXTRA RELEVANT SYSTEM CONFIGURATION :
Machine running with an Intel i7 920 processor (4 physical cores with hyperthreading = 8 virtual cores)

A DESCRIPTION OF THE PROBLEM :
Upon deserialization of java.util.Calendar instances by multiple threads, the following exception is raised:

java.lang.ClassCastException: java.util.SimpleTimeZone cannot be cast to sun.util.calendar.ZoneInfo
  at java.util.Calendar$1.run(Calendar.java:2653)
  at java.util.Calendar$1.run(Calendar.java:2651)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.util.Calendar.readObject(Calendar.java:2650)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  at java.lang.reflect.Method.invoke(Method.java:597)
  at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
  at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
  at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
  at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
  at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
  at CalendarSerialization$DeserializationThread.run(CalendarSerialization.java:80)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Save the attached code in CalendarSerialization.java
- compile with "javac CalendarSerialization.java"
- execute with "java -cp . CalendarSerialization."

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No error or exception should be displayed
ACTUAL -
One or more instances of "java.lang.ClassCastException: java.util.SimpleTimeZone cannot be cast to sun.util.calendar.ZoneInfo" are raised, with the corresponding stack trace

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.ClassCastException: java.util.SimpleTimeZone cannot be cast to sun.util.calendar.ZoneInfo
  at java.util.Calendar$1.run(Calendar.java:2653)
  at java.util.Calendar$1.run(Calendar.java:2651)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.util.Calendar.readObject(Calendar.java:2650)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  at java.lang.reflect.Method.invoke(Method.java:597)
  at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
  at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
  at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
  at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
  at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
  at CalendarSerialization$DeserializationThread.run(CalendarSerialization.java:80)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.*;
import java.util.Calendar;

/**
 * Test of multithreaded serialization/deserialization of Calendar.
 * @author Laurent Cohen
 */
public class CalendarSerialization
{
  public static void main(String[] args)
  {
    try
    {
      new CalendarSerialization().perform();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  public void perform() throws Exception
  {
    int nbThreads = 8;
    Calendar cal = Calendar.getInstance();
    SerializationThread[] threads = new SerializationThread[nbThreads];
    for (int i=0; i<nbThreads; i++) threads[i] = new SerializationThread(cal);
    for (int i=0; i<nbThreads; i++) threads[i].start();
    for (int i=0; i<nbThreads; i++) threads[i].join();
    DeserializationThread[] threads2 = new DeserializationThread[nbThreads];
    for (int i=0; i<nbThreads; i++) threads2[i] = new DeserializationThread(threads[i].data);
    for (int i=0; i<nbThreads; i++) threads2[i].start();
    for (int i=0; i<nbThreads; i++) threads2[i].join();
  }

  public class SerializationThread extends Thread
  {
    private Calendar cal;
    public byte[] data;

    public SerializationThread(Calendar cal)
    {
      this.cal = cal;
    }

    public void run()
    {
      try
      {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(cal);
        oos.flush();
        oos.close();
        data = baos.toByteArray();
      }
      catch (Exception e)
      {
        e.printStackTrace();
      }
    }
  }

  public class DeserializationThread extends Thread
  {
    public Calendar cal;
    public byte[] data;

    public DeserializationThread(byte[] data)
    {
      this.data = data;
    }

    public void run()
    {
      try
      {
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        ObjectInputStream ois = new ObjectInputStream(bais);
        cal = (Calendar) ois.readObject();
        ois.close();
      }
      catch (Exception e)
      {
        e.printStackTrace();
      }
    }
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I do not know of any workaround

                                    

Comments
EVALUATION

Calendar isn't thread-safe by design. Any Calendar instances shouldn't be shared by threads.
                                     
2011-02-07
WORK AROUND

Add synchronization with `cal' in SerializationThread.run(), like this:

                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                synchronized (cal) {
                    ObjectOutputStream oos = new ObjectOutputStream(baos);
                    oos.writeObject(cal);
                    oos.flush();
                    oos.close();
                }
                data = baos.toByteArray();
                                     
2011-02-07
EVALUATION

Calendar.writeObject will be made synchronized as a workaround fix. But in general Calendar is thread-unsafe even with only getter calls.
                                     
2012-01-26



Hardware and Software, Engineered to Work Together