SimpleDateFormat and Thread Safety

SimpleDateFormat and Thread Safety

It never fails to surprise me that so many developers are unaware that SimpleDateFormat is not thread-safe. It seems like almost all J2EE projects I work on have code that uses instance variables or static instance variables to store a SimpleDateFormat that is then used throughout the code base without any concurrency control. Here’s a classic example:

public class MyService // could be a web service or a session bean
{
 public void someBusinessMethod(String datestring, ...) {
     Date date = GlobalConst.DATE_FMT.parse(datestring);
     // rest of code omitted
    }
}

This is particularly nasty because the code may well run without producing any exceptions but could easily parse the date incorrectly. If you don’t believe how likely this is, here’s some code to prove the point:

public class ProveNotSafe {
 static SimpleDateFormat df
            = new SimpleDateFormat( "dd-MMM-yyyy" );
 static String testdata[] = {
     "01-Jan-1999", "14-Feb-2001", "31-Dec-2007"
 };
 public static void main(String[] args) {
     Runnable r[] = new Runnable[ testdata.length ];
     for (int i = 0; i < r.length; i++) {
         final int i2 = i;
         r[i] = new Runnable() {
             public void run() {
                 try {
                     for (int j=0; j<1000; j++) {
                         String str = testdata[i2];
                         String str2 = null;
                         /*synchronized(df)*/ {
                             Date d = df.parse(str);
                             str2 = df.format( d );
                         }
                         if (!str.equals(str2)) {
                             throw new RuntimeException(
                                 "date conversion failed after "
                               + j + " iterations. Expected "
                               + str + " but got " + str2 );
                         }
                     }
                 } catch (ParseException e) {
                     throw new RuntimeException( "parse failed" );
                 }
             }
         };
         new Thread(r[i]).start();
     }
 }
}

When I run this code, I get the following output:

java.lang.RuntimeException: date conversion failed after 3 iterations.
Expected 14-Feb-2001 but got 01-Dec-2007

Note that “01-Dec-2007″ isn’t even one of the strings in the test data. It is actually a combination of the dates being processed by the other two threads!

If I uncomment the synchronized keyword then of course it runs without exception.

If I want to quickly remove these issues from a code base I usually create a simple replacement class like this that wraps SimpleDateFormat.

public class ThreadSafeSimpleDateFormat {
private DateFormat df;

public ThreadSafeSimpleDateFormat(String format) {
this.df = new SimpleDateFormat(format);
}

public synchronized String format(Date date) {
return df.format(date);
}

public synchronized Date parse(String string) throws ParseException {
return df.parse(string);
}
}

Other Posts

  • garikapati

    I had this issue in my recent project and wasted couple of valuable working days by not knowing where is the problem.
    Finally I got this culprit and fixed the issue.
    I think it’s best to make code block as synchronized than a method… in ThreadSafeSimpleDateFormat class?
    what’s your say?

  • Li

    I made a class that can make the work easier. Please check at: http://li-ma.blogspot.com/2007/10/thread-safe-date-formatparser.html

  • Li

    Sorry, the link got cut in my previous post. Please check here.
    Or it is the first post in my blog at:
    http://li-ma.blogspot.com