I recently got a note on another blog post asking this question:
I have a general question on the thread safety and this is not directly related with your blog. I would appreciate if you could post it on your blog. I have a class that has only one static method that accepts two string parameters and does some operation locally (as shown below). Do I need to synchronize this method?
public class A {
public static boolean getDate(String str, String str2){
SimpleDateFormat formatter =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
boolean isBefore = false;
Date date1;
Date date2;
try {
date1 = formatter.parse(str);
date2 = formatter.parse(str);
isBefore = date1.after(date2);
} catch (Exception e) {
e.getMessage();
}
return isBefore;
}
}
Since it had nothing to do with the blog post in question, I'll answer it here.
The questioner is clearly asking about the well-documented lack of thread safety of the Format classes. This is a long-standing bug at Sun. If you create a Format object (or a MessageFormat, NumberFormat, DecimalFormat, ChoiceFormat, DateFormat or SimpleDateFormat object), it cannot be shared among threads. The above code does not share its SimpleDateFormat object among threads, so it is safe.
If the formatter field had been a static field of the class, the method would not be thread-safe. However, this way, you have to create a (relatively) expensive SimpleDateFormat object every time you invoke the method. There are many ways around this. One answer (if you want to avoid locking) is to use a ThreadLocal:
I have a general question on the thread safety and this is not directly related with your blog. I would appreciate if you could post it on your blog. I have a class that has only one static method that accepts two string parameters and does some operation locally (as shown below). Do I need to synchronize this method?
public class A {
public static boolean getDate(String str, String str2){
SimpleDateFormat formatter =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
boolean isBefore = false;
Date date1;
Date date2;
try {
date1 = formatter.parse(str);
date2 = formatter.parse(str);
isBefore = date1.after(date2);
} catch (Exception e) {
e.getMessage();
}
return isBefore;
}
}
Since it had nothing to do with the blog post in question, I'll answer it here.
The questioner is clearly asking about the well-documented lack of thread safety of the Format classes. This is a long-standing bug at Sun. If you create a Format object (or a MessageFormat, NumberFormat, DecimalFormat, ChoiceFormat, DateFormat or SimpleDateFormat object), it cannot be shared among threads. The above code does not share its SimpleDateFormat object among threads, so it is safe.
If the formatter field had been a static field of the class, the method would not be thread-safe. However, this way, you have to create a (relatively) expensive SimpleDateFormat object every time you invoke the method. There are many ways around this. One answer (if you want to avoid locking) is to use a ThreadLocal:
private static final ThreadLocal formatters =
new ThreadLocal() {
@Override public SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
}
};
public static boolean getDate(String str, String str2) {
SimpleDateFormat formatter = formatters.get();
...
The even better answer is to use the thread-safe Joda time libraries, instead. I hate to recommend using a non-standard library when there is an equivalent standard one, but this Format behavior is pretty broken.
Comments
Of course, making formatter global and put in threadlocal run a lot faster.
Silly me, i thought i would be ok just making the calls synchronized. Now i switched to Joda time, problem solved.
i so wish that Jsr-310 can make it to JDk7 but my hopes are not so high.
Sorry I can't be more helpful. In an ideal world, everyone would document API thread-safety.
I've been using ThreadLocal since I read THE Java Currency book, so get used to it!
A Professional Java Developer
As for initializing with a variable, I can think of several solutions, but it really depends on your code.
new DateFormat() {
private SimpleDateFormat _df =..;
public synchronized String format(...) {
return _df.format(..);
}
..
}
If the cost of building/rebuilding is too high.. then maybe the cost of synchronizing isn't.
Of course you'll lose concurrency..
When you have a typesafe way of doing something, relying on clever tricks isn't always the best way to go.