August 30, 2007

Are your Java projects misusing java.text.Format?

As a consultant with ThoughtWorks I get the opportunity to see many different code bases at a variety of client. A common mistake I see again and again is that developers do not realize that sub-classes of java.text.Format (e.g. NumberFormat, SimpleDateFormat) are not thread safe. If your project defines a static instance of a formatter, like so:
public static final SimpleDateFormat SIMPLE_DATE_FORMAT 
= new SimpleDateFormat("MM-dd-yyyy");

Then chances are you have a bug.

As is the case with many threading issues, this might not be easily identified because it probably doesn't happen consistently (we are at the mercy of the Thread Scheduler).

Recently on a project I created a test to prove that using such Formats can cause problems. We then fixed the issue and decided to keep the test in the code base to ensure that no developers 'accidentally' re-introduces it. NOTE: A Continuous Integration server, CruiseControl, ensures that our tests are run continuously.

Below is a simple, self-contained, test you can use to see the issue for yourself. Remember that the test might not fail every time because it is dependent on how the threads are scheduled. I have found that the test fails with more consistency when executed a single CPU (or single-core) machine.

import junit.framework.TestCase;

import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadSafetyTest extends TestCase {
private static final SimpleDateFormat FORMAT
= new SimpleDateFormat();

public void testSimpleDateFormat() throws InterruptedException {
for (int i = 0; i < 50; i++) {
MyRunnable[] runnables = {
new MyRunnable(new Date(-1)),
new MyRunnable(new Date(1))
};

Thread one = new Thread(runnables[0]);
Thread two = new Thread(runnables[1]);

one.start();
two.start();

one.join();
two.join();

assertFalse("Values should not be equal",
runnables[0].value.equals(runnables[1].value));
}
}

private static class MyRunnable implements Runnable {
private Date date = null;
public String value = null;

MyRunnable(Date date) {
this.date = date;
}

public void run() {
value = FORMAT.format(date);
}
}
}

2 comments:

CARFIELD said...

Not only DateFormat, NumberFormat and Calendar Object are not thread safe. Usually it is not good to use static to prevent object creation.

Michael Ward said...

Your exactly right, that is why the second sentence mentions "sub-classes of java.text.Format". DateFormat was used just to illustrate the point. Thanks for your comments.