DateFormat class in a multithreaded application
I am using the DateFormat class to parse a date from a String. It works fine alomost all the time but I then sometimes get
the following exception:
java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(Unknown Source)
at java.lang.Long.parseLong(Unknown Source)
at java.lang.Long.parseLong(Unknown Source)
at java.text.DigitList.getLong(Unknown Source)
at java.text.DecimalFormat.parse(Unknown Source)
at java.text.SimpleDateFormat.subParse(Unknown Source)
at java.text.SimpleDateFormat.parse(Unknown Source)
at java.text.DateFormat.parse(Unknown Source)
I read in the following posting that the DateFormat class is not thread-safe: http://forum.java.sun.com/thread.jspa?threadID=497693&messageID=2906187
In my situation I have a singleton class which is used by multiple threads at the same time. The DateFormat variable is
an instance-level variable. But the date format is not changed by any thread. It is the same for all threads. Therefore,
I am thinking that the problem described in the above forum posting isn't the actual cause of my exception?
Would appreciate other views on this because ....
I cannot reproduce the issue so it is difficult to find out exactly what is causing it. However, not sure whether or not this is just a coincidence but another problem I encountered on the same run seems to be that the
Date close = standardDateFormat.parse(accountDate);
seemed to get completely mixed up between the threads. So there could be a threading issue but I'm not sure why!!
standardDateFormat is a private instance level variable which does not change. A constant is used to set the date format.
accountDate is a local variable.
When I reran the program with the exact same data a few hours later it was inserted correctly this time. No change was made to the program in the meantime.
Many Thanks
# 1
> The DateFormat variable is an instance-level variable. But the date format is not changed by any thread.Hi,the problem is because string is emtpy i.e. another thread have changed the refference of accountDate...
# 2
Thanks Dima,
so the date from thread 1 is passed to the parse method and the parameter of this method refers to date1. Then another date is passed from thread 2 to the parse method of the same DateFormat object.
And since the prse method isn't synchronized the date that one thread is working on could be changed by another thread before the original thread has completed. So thread 1 could end up getting the result (parsed date) of the date being parsed by thread 2.
Is my understanding correct?
Thanks again
# 3
> the problem is because string is emtpy i.e. another thread have changed the refference of accountDate...
This is not possible.
DateFormat has internal instance data and no synchronization, so it isn't thread-safe. You either have to synchronise all calls to it or use a different DateFormat per thread, e.g. by allocating it as a local variable inside the singleton's method(s) rather than as instance data of the singleton.
ejpa at 2007-7-12 2:20:40 >

# 4
> so the date from thread 1 is passed to the parse method and the parameter of this method refers to date1. Then another date is passed from thread 2 to the parse method of the same DateFormat object.
Not exectly ;()
This way should not cause problem. But!
If You have two threads working with the same variable, let say myString, T1 may set it to "somedate" then T2 set it to "" and then T1 call DateFormat with myString which is "" by now.
# 5
This has been a longstanding issue. IMO it is appalling that java.text.DateFormat's parse and format methods are not thread-safe, and Sun's engineers should be ashamed of not having fixed this until now!
These functions are per se stateless, it cannot be a problem to make them independent of internal object state.
Note that this does not mean that all of DateFormat's properties (setLenient, setTimezone, etc.) would need to be thread-safe.
That said, in our projects we helped ourselves by providing static wrapper methods that allowed for thread-safe usage of DateFormats:
public static DateFormat synchronizedDateFormat(final DateFormat format) {
return new DateFormat() {
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
synchronized(format) {
return format.format(date, toAppendTo, fieldPosition);
}
}
@Override
public Date parse(String source, ParsePosition pos) {
synchronized(format) {
return format.parse(source, pos);
}
}
};
}
# 6
> This has been a longstanding issue. IMO it is
> appalling that java.text.DateFormat's parse and
> format methods are not thread-safe, and Sun's
> engineers should be ashamed of not having fixed this
> until now!
I think that as always multithreading is up to the people who write the code. The Javadoc documents that DateFormat is not thread safe, and it offers a proactive suggestion on how to use that in a multithreading environment.
[quote from Javadocs]
Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.
[/quote]
Once it's documented, than it's the responsibility of the developer (like you guys did at your company) to provide a solution.
# 7
> IMO it is appalling that java.text.DateFormat's parse and format methods are not thread-safe
They can't be thread-safe, because they are interdependent. The parsing depends on the format. So synchronizing either of those methods wouldn't help., and neither would synchronizing them internally. The caller has to synchronize at a higher level. Same argument applies as for Vector: it was/is synchronized but in practice this isn't very useful, hence ArrayList isn't.
> That said, in our projects we helped ourselves by
> providing static wrapper methods that allowed for
> thread-safe usage of DateFormats:
Why don't you just use a DateFormat object per thread like everyone else does, and as Sun clearly intended, and save yourself the pointless blocking?
ejpa at 2007-7-12 2:20:41 >

# 8
> They can't be thread-safe, because they are
> interdependent. The parsing depends on the format. So
> synchronizing either of those methods wouldn't help.,
> and neither would synchronizing them internally.
Of course I know that the parsing depends on the DateFormat's properties. I just don't agree with your conclusion. The common use case for DateFormat comprises (in that order)
- creating an instance.
- setting the properties you need.
- Using the instance multiple times to parse or format dates.
A parser or formatter transforms its input argument(s) into a return value. There's no need for it to have any side-effects.
Thus, it would make perfect sense to document the API as "as long as no properties of the DateFormat are being changed, a DateFormat's parse and format methods may be invoked from multiple threads without further synchronization."
> Why don't you just use a DateFormat object per thread
> like everyone else does, and as Sun clearly intended,
> and save yourself the pointless blocking?
Of yourse I can do "like everyone else does", but alteratively, I can think a little further and try to point out a quirk in the JDK: apparently stateless functions that break in a multithreaded context.
That may have been intended by Sun, but it's still a design flaw ;-)
# 9
The trend in the JDK is to remove synchronization, not add it, as per the Vector/ArrayList example.
I wouldn't t describe DateFormat as being stateless when it has a locale and a format pattern.
But on a quick look I don't see anything in DateFormat.java or SimpleDateFormat.java other than the format pattern and the locale that would stop it being threadsafe in practice given no changes in those attributes. Specifically I don't see any parse state being held anywhere as instance or class data.
ejpa at 2007-7-12 2:20:41 >

# 10
> I wouldn't t describe DateFormat as being stateless
> when it has a locale and a format pattern.
I tried to explain that this is not about DateFormat being stateless (how can any object be stateless, BTW?), but about DateFormat.parse() and DateFormat.format() being stateless (although dependant on DateFormat's other properties), with the common definition of stateless as "two consecutive invocations of a function with the same arguments yield the same result"
> But on a quick look I don't see anything in
> DateFormat.java or SimpleDateFormat.java other than
> the format pattern and the locale that would stop it
> being threadsafe in practice given no changes in
> those attributes. Specifically I don't see any parse
> state being held anywhere as instance or class data.
See? Even you wouldn't expect any parse state to be necessary ;-)
But, stupid enough, there is: SimpleDateFormat changes the member variable calendar on every parse.
Please note that this is not even properly documented: There is a getter setter and a setter for this calendar. So the documentation for getCalendar() should at least mention "the value returned is not necessarily the value set by the last call to setCalendar(), since it may change during every call to parse()
# 11
>
> I tried to explain that this is not about
> DateFormat being stateless (how can any object
> be stateless, BTW?),
From Java Concurrency in practice (referred to a Stateless servlet):
[quote]
[Stateless objects] have no fields and references no fields from other classes. The transient state for a particular computation exists solely in local variables that are stored on the thread's stack and are accessible only to the executing thread. [...] Since the actions of a thread accessing a stateless object cannot affect the correctness of operations in other threads, stateless objects are thread-safe
[/quote]
Message was edited by:
mtedone
# 12
OK well spotted. But ...
> the common definition of stateless as "two consecutive invocations of a function with the same arguments yield the same result"
That's the definition of idempotence actually: not quite the same thing.
But I still don't know what the problem is with taking Sun's explicit advice and having an instance per thread, instead of beating your head against a wall. The clear JDK trend is away from thread-safe objects, and has been since last century. Nobody is ever going to fix this one for you the way you like it and what with the interdependency of format and pattern I don't see how they could.
If you think you have a good case to the contrary file an RFE. But I know what I would say if it came across my desk: 'Working as designed: close'.
ejpa at 2007-7-12 2:20:41 >

# 13
>But I still don't know what the problem is with taking Sun's explicit >advice and having an instance per thread, instead of beating your >head against a wall.
OK, obviously I coudn't convey my arguments. So maybe I'm wrong then, after all. And my head is really starting to hurt after all that beating ;-)
> But I know what I would say if it came
> across my desk: 'Working as designed: close'.
...and I know what I'd say if code like that came across my desk:
'Unnecessary changes to a member variable in a function that should work only on its input arguments: Rejected'
# 14
> ...and I know what I'd say if code like that came
> across my desk:
> 'Unnecessary changes to a member variable in a
> function that should work only on its input
> arguments: Rejected'
So you write all of your code, every single bit, as though it was always going to be used in a thread?
I know that if I review code and it is being written to work in a thread environment when the requirements specifically don't call for that then the developer is spending time doing something that isn't needed. And that costs money.
Given that not all my applications require threads and that I require some of them to be fast, I don't want the general libraries that all java code depends on to be written for a stricter environment simply because some people don't want to create extra instances or sync them.
By the way why specifically do you think this is a problem? I have timed instance creation of this class and it is very fast. And if it isn't fast enough the just create a state variable for each thread and keep your instance there.
