Bug ID: JDK-4244166 “Calendar.getInstance()” and “new Date()” can deadlock.


Name: vi73552			Date: 06/04/99


We noticed a deadlock between java.util.Calendar, 
and java.util.Date.  If neither class has ever been loaded,
and thread A calls Calendar.getInstance(), and thread B 
simultaneously creates a new Date, the threads can deadlock.

Obviously, this only happens if you get the race condition
just exactly right, but I have written a simple program
illustrating the problem that the classes get themselves
into:

Program:

import java.util.Hashtable;

/**
 * Demonstrates a deadlock that can occur if one thread is
 * calling Calendar.getInstance() for the first time ever
 * while another thread is creating the first Date ever.
 *
 * Simplified classes are used to illustrate the bug and
 * to force the race condition to happen the right way.
 *
 * If you replace the fake classes with the real ones from
 * the java.util package, then you can also see it, but
 * only if you're "lucky."
 */
public class DateCalendarDeadlock
{
    public static void sleep(long millis)
    {
        try
        {
            Thread.sleep(millis);
        }
        catch (Exception ex)
        {
        }
    }

    public static void main (String [] args) throws Exception
    {
        System.out.println("Creating threads.");

        Thread threadA = new Thread()
        {
            public void run()
            {
                MyCalendar.getInstance();
                System.out.println("Thread A: Exiting");
            }
        };

        Thread threadB = new Thread()
        {
            public void run()
            {
                DateCalendarDeadlock.sleep(5 * 1000);
                System.out.println("Thread B: Started, calling new MyDate()...");

                MyDate myDate = new MyDate();

                System.out.println("Thread B: Exiting");
            }
        };

        threadA.start();
        threadB.start();

        threadA.join();
        threadB.join();

        System.out.println("Program completed normally.");
    }
}

// Vastly simplified version of java.util.Calendar
class MyCalendar
{
    // Comment out the 'synchronized' and the deadlock disappears...
    public static synchronized MyCalendar getInstance()
    {
        System.out.println("Thread A: Acquired MyCalendar lock; waiting for Thread B to start...");

        // We've got MyCalendar locked, so wait for Thread B to deadlock.
        DateCalendarDeadlock.sleep(10 * 1000);

        System.out.println("Thread A: Calling new MyGregorianCalendar()...");

        // Thread A gets deadlocked here, because Thread B has MyGregorianCalendar locked...
        return new MyGregorianCalendar();
    }
}

// Vastly simplified version of java.util.GregorianCalendar
class MyGregorianCalendar extends MyCalendar
{
    public MyGregorianCalendar()
    {
        // Thread B gets deadlocked here, trying to call the
        // MyCalendar constructor.
        super();

        System.out.println("The deadlock did not occur.");
    }
}

// Vastly simplified version of java.util.MyDate
class MyDate
{
    static MyCalendar staticCal;

    static
    {
        System.out.println("Thread B: In MyDate static constructor, calling new MyGregorianCalendar()...");
        staticCal = new MyGregorianCalendar();
    }

    public MyDate()
    {
        System.out.println("Thread B: In MyDate Instance Constructor.");
    }
}

Output:

PROMPT: java -version
java version "1.1.7B"

PROMPT: java -fullversion
java full version "JDK1.1.7U"

PROMPT: java DateCalendarDeadlock
Creating threads.
Thread A: Acquired MyCalendar lock; waiting for Thread B to start...
Thread B: Started, calling new MyDate()...
Thread B: In MyDate static constructor, calling new MyGregorianCalendar()...
Thread A: Calling new MyGregorianCalendar()...
^C (Had to CTRL-C out of it, program was hung...)
(Review ID: 83919) 
======================================================================