LendKey

Thursday, October 18, 2007

A Thread Safe Date Formatter/Parser

I have seen many different ways to use DateFormat classes(most ly SimpleDateFormat) from different people including myself, and unfortunately, most of them are either buggy or not efficient.
The following are the most common scenarios I saw:
1. Use a static final formatter in different thread:
static final private DateFormat df = new SimpleDateFormat("yyyy/MM/ddd");
...
void myMethod()
{
df.parse(..)
df.format(..)
}
People, including me, were thinking to reduce number of instances of df and reuse it everywhere. But this is dead wrong! The JavaDoc has clearly mentioned that SimpleDateFormat is NOT thread-safe. So once you put multiple threads using the same instance, strange result will happen. Most of the time, people tent to ignore the warning from JavaDoc or don't even know it. And most of time, the code works ok, because after all, there is not that many threads running in parallel if you are only doing a small website.
2. Create DateFormat instance eveyrtime it is used
void myMethod()
{
DateFormat df= new SimpleDateFormat("yyyy/MM/dd");
df.parse(..);
df.format(..);
}
This works without bug, but you can tell lots of df objects will be created, they I always feel it is not good to create extra objects when you are coding for large and heavily loaded website.
3. Apache Jakarta Common Lang's FastDateFormat
http://www.jdocs.com/lang/2.1/org/apache/commons/lang/time/FastDateFormat.html
This is a thread-safe solution. But the problem is still you have to create each instance for different pattern. Many times in your code, you have to create multiple instance of the same pattern at many different places, which mean multiple different objects to do the same thing. The biggest problem with FastDateFormat is it only format and does not do parsing.
So after spending about an hour or so doing research, I came up with my own solution:
ThreadSafeDateParser.
Here's the class:

And the test Class:



Idea of the class is:
  1. Very easy to use. Just call those static methods and provide a string and pattern. The class will handle everything else, including formatter object creation, thread mangement, etc.
  2. For same patter and same thread, the class create only one instance of DateFormat, so you got thread-safe and use the minimum system resource
I tested the class with 1000 threads running at the same time, and it works solid with out any problem.

Hopefully this is something lots of people can reuse in your code.
I know I do not have locale and timezone handling yet. I you have any comment or suggestion, feel free to share.

Enjoy!

Li

11 comments:

aaronmagi said...

Your post does not allow you to see the code you actually posted.

Are you able to email it to me please

aaronmagi AT hotmail.com

thanks

I am quite interested in seeing your solution as we are currently looking for a solution to fastDateFormatter but with the ability to use parse.

Christoph Kutzinski said...

Looks good. I just have two minor points:
a) why are you using Hashtable (which uses superfluous synchronisation) instead of HashMap?
b) you *might* want to use SoftReferences to hold the DateFormats in the map. But that's just in the unusual case that you can have dynamic changing formats

Allen said...

Is there any reason why you are not using a HashMap ?

Jay Khimani said...

Hey Li,

Thanks for this nice clean code. Its very useful

Cheers !!!
- Jay

Allen said...

Will the use of ThreadLocal cause a problem if you are using a thread pool in a web container (Websphere in my case)? Is there a way to implement this without using ThreadLocal?

Thanks

Allen

Li Ma said...

I finally made correction Christoph and AllenCong asked earlier, that I should have used HashMap instead of Hashtable in the code.

As for Allen's question regarding to thread pool in web container, I don't think there should be any issue.

Allen, please do let me know if you run into any problem in WebSphere.

Thanks!

Li

Allen said...

Li,

The reason I ask about using ThreadLocal in a web container is this IBM article http://www-01.ibm.com/support/docview.wss?uid=swg21368248. We are seeing extra native memory use at the moment (ending in out of memory errors) and this code is one of the changes that has been made.

Thanks for your fast response.

Allen

Li Ma said...

Allen,

I never had chance to test this code in WebSphere. But from the URL you sent, seams like it could cause issue because of usage of ThreadLocal.

Since I'm not experience with WebSphere at all, is it possible to follow the suggestion from that page, which is to set thread pool to a fixed number?

I cannot believe IBM could not fix such a critical issue in their container or JDK.

Anyway, thanks for the feedback.

Cheer!

Li

Allen said...

It doesn't appear to just be WebSphere. It seems that ThreadLocal is not designed to run in a Web Container at all. Have a look at this page http://blog.maxant.co.uk/pebble/2008/09/23/1222200780000.html. It is a problem with the ThreadLocal implementation by Sun.

Ondrej Medek said...

Hi, I cannot see your code.

Beware, ThreadLocal (especially static or using inner class) can cause memory leaks in a web container, when the application is redeployed.

Ondrej Medek said...

I think the best solution is to have a static prototype and clone() the parser before you use it.

Creating Objects in Java is very fast, even faster, then malloc() in C. The only problem with creating an instance of SimpleDateFormat is that the constructor has to parse the format String. The clone() method does not do it, so it's very fast.


private static final DateFormat dateFormatterPrototype = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz", Locale.ROOT);
static {
dateFormatterPrototype.setTimeZone(TimeZone.getTimeZone("GMT"));
}

// usage
((SimpleDateFormat)dateFormatterPrototype.clone()).format(new Date()));