271

Please tell with a code example why is SimpleDateFormat not threadsafe. What is the problem in this class? Is The problem with format function of SimpleDateFormat? Please give a code which demonstrates this fault in class.

FastDateFormat is threadsafe. Why? what is the difference b/w the SimpleDateFormat and FastDateFormat?

Please explain with a code which demonstrates this issue?

6
  • 2
    FastDateFormat is a commons-lang class: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… Commented Sep 2, 2014 at 16:33
  • 5
    Most Developers understand that for most classes that are not thread safe, that this is due to concurrently changing state. Once a Format is established, formatting a Date should not change state. Simply documenting this in official documentation as not thread-safe is not enough. It should be explicitly documented that even the format method is not thread-safe if it maintains temporary state in instance variables. Declaring it as static is not just a rookie mistake. Analogy can be made between modifying a collection (put) vs accessing a collection (get). Commented Nov 2, 2014 at 5:26
  • 1
    Just a short real story: I've running a cloud based application for about 8 years, with nearly 100% uptime. There were a strange individual error recently related to parsing dates. One parsed date was wrong. During a code review I discovered that SimpleDateFormat was used wrong and it was a thread-safety issue. One error for 8 years! Of course I'm going to fix it. Commented Oct 13, 2018 at 13:49
  • I made the same error too, expecting the format and parse methods to be threadsafe once the format and timezone are set. Currently I am searching and fixing all those SimpleDateFormat usages in our codebase :/ Commented Aug 19, 2020 at 7:28
  • 1
    Never use DateFormat, SimpleDateFormat, Date, and Calendar classes. These terrible classes are all legacy now. They were supplanted years ago by the modern java.time classes defined in JSR 310. The java.time classes are thread-safe by design, using immutable objects. Commented Mar 20, 2021 at 23:36

9 Answers 9

282

SimpleDateFormat stores intermediate results in instance fields. So if one instance is used by two threads they can mess each other's results.

Looking at the source code reveals that there is a Calendar instance field, which is used by operations on DateFormat / SimpleDateFormat.

For example parse(..) calls calendar.clear() initially and then calendar.add(..). If another thread invokes parse(..) before the completion of the first invocation, it will clear the calendar, but the other invocation will expect it to be populated with intermediate results of the calculation.

One way to reuse date formats without trading thread-safety is to put them in a ThreadLocal - some libraries do that. That's if you need to use the same format multiple times within one thread. But in case you are using a servlet container (that has a thread pool), remember to clean the thread-local after you finish.

To be honest, I don't understand why they need the instance field, but that's the way it is. You can also use joda-time DateTimeFormat which is threadsafe.

Sign up to request clarification or add additional context in comments.

12 Comments

They don't need the instance field; it is undoubtedly the result of sloppy programming in a misguided attempt at efficiency. The really mind-boggling thing is that this trap-door wasn't nailed shut long long ago. I think the real answer is to avoid java.util.Date and Calendar.
Has this been fixed in JDK8? If no, then why not?
this hasn't been fixed in JDK8 per se. but JDK8 introduces the new java.time package, including DateTimeFormatter which is threadsafe.
It cannot be "fixed" ever, without breaking backwards compatibility. It's better to leave it alone, and let new code just use the newer, thread-safe alternatives. .
@Enerccio ...then in theory, yes, that won't break anything. In practice, I guarantee that in the decades (yes, literally decades, SimpleDateFormat has been around since 1.1, which was released in 1996, more than 20 years ago) since, plenty of people have depended on the lack of thread-safety for something or other. Is it horrible practice? Yes, obviously. Does that mean it doesn't happen? I wish.
|
72

SimpleDateFormat is a concrete class for formatting and parsing dates in a locale-sensitive manner.

From the JavaDoc,

But 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.

To make the SimpleDateFormat class thread-safe, look at the following approaches :

  • Create a new SimpleDateFormat instance each time you need to use one. Although this is thread safe, it is the slowest possible approach.
  • Use synchronization. This is a bad idea because you should never choke-point your threads on a server.
  • Use a ThreadLocal. This is the fastest approach of the 3 (see http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html).

7 Comments

This looks like a good summary but I disagree with the author's second point. Somehow I doubt synchronizing a date format is going to be the choke point on your server. Is this, per Knuth, one of the 3% of cases where premature optimization is required, or does it fall into the 97%, where "we should forget about small inefficiencies"? Now, I've seen folks, with custom web frameworks, wrapping controller in a synchronized block, and thus all access beyond including database calls, business logic - and then spending a huge effort on performance testing. No wonder there, they are in the 3%.
@michaelok I have to concur! I think it is just the other way around - using one Single Dateformatter instead of creating a new one whenever you need it is premature optimization. You should do the Easy thing first: Just use a new Instance whenever you need it. - And only if this becomes a performance-problem (Memory, GBC) then you should think about a shared instance - but remember: Anything you share between Threads can become a silent race-condition waiting to suckerpunch you.
And btw. an easy point could be one Thread getting stuck in the Routine of the Dateformatter because of whatever Problems - and suddenly EACH AND EVERY Thread on your webserver would get stuck, when they try to access the DateFormatter... DED ;-)
You can create a new instance or clone it that can be a little bit faster.
What was the problem - you ran into a chokepoint due to synchronizing the date format? Or an issue with thread-safety? One thing as mentioned above by James Turner is that Java 8's Formatter is thread safe: docs.oracle.com/javase/8/docs/api/java/time/format/…
|
69

DateTimeFormatter in Java 8 is immutable and thread-safe alternative to SimpleDateFormat.

3 Comments

yes but you'll have to go with a Temporal (LocalDate, LocalDateTime, ...) instead of java.util.Date which SimpleDateFormat uses.
@SaadBenbouzid Consider that an advantage. The modern classes are so much nicer to work with than the outmoded Date class and offer much more possibilities.
Yep and have problems with offset.
35

ThreadLocal + SimpleDateFormat = SimpleDateFormatThreadSafe

package com.foocoders.text; import java.text.AttributedCharacterIterator; import java.text.DateFormatSymbols; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; public class SimpleDateFormatThreadSafe extends SimpleDateFormat { private static final long serialVersionUID = 5448371898056188202L; ThreadLocal<SimpleDateFormat> localSimpleDateFormat; public SimpleDateFormatThreadSafe() { super(); localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat(); } }; } public SimpleDateFormatThreadSafe(final String pattern) { super(pattern); localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat(pattern); } }; } public SimpleDateFormatThreadSafe(final String pattern, final DateFormatSymbols formatSymbols) { super(pattern, formatSymbols); localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat(pattern, formatSymbols); } }; } public SimpleDateFormatThreadSafe(final String pattern, final Locale locale) { super(pattern, locale); localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat(pattern, locale); } }; } public Object parseObject(String source) throws ParseException { return localSimpleDateFormat.get().parseObject(source); } public String toString() { return localSimpleDateFormat.get().toString(); } public Date parse(String source) throws ParseException { return localSimpleDateFormat.get().parse(source); } public Object parseObject(String source, ParsePosition pos) { return localSimpleDateFormat.get().parseObject(source, pos); } public void setCalendar(Calendar newCalendar) { localSimpleDateFormat.get().setCalendar(newCalendar); } public Calendar getCalendar() { return localSimpleDateFormat.get().getCalendar(); } public void setNumberFormat(NumberFormat newNumberFormat) { localSimpleDateFormat.get().setNumberFormat(newNumberFormat); } public NumberFormat getNumberFormat() { return localSimpleDateFormat.get().getNumberFormat(); } public void setTimeZone(TimeZone zone) { localSimpleDateFormat.get().setTimeZone(zone); } public TimeZone getTimeZone() { return localSimpleDateFormat.get().getTimeZone(); } public void setLenient(boolean lenient) { localSimpleDateFormat.get().setLenient(lenient); } public boolean isLenient() { return localSimpleDateFormat.get().isLenient(); } public void set2DigitYearStart(Date startDate) { localSimpleDateFormat.get().set2DigitYearStart(startDate); } public Date get2DigitYearStart() { return localSimpleDateFormat.get().get2DigitYearStart(); } public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) { return localSimpleDateFormat.get().format(date, toAppendTo, pos); } public AttributedCharacterIterator formatToCharacterIterator(Object obj) { return localSimpleDateFormat.get().formatToCharacterIterator(obj); } public Date parse(String text, ParsePosition pos) { return localSimpleDateFormat.get().parse(text, pos); } public String toPattern() { return localSimpleDateFormat.get().toPattern(); } public String toLocalizedPattern() { return localSimpleDateFormat.get().toLocalizedPattern(); } public void applyPattern(String pattern) { localSimpleDateFormat.get().applyPattern(pattern); } public void applyLocalizedPattern(String pattern) { localSimpleDateFormat.get().applyLocalizedPattern(pattern); } public DateFormatSymbols getDateFormatSymbols() { return localSimpleDateFormat.get().getDateFormatSymbols(); } public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) { localSimpleDateFormat.get().setDateFormatSymbols(newFormatSymbols); } public Object clone() { return localSimpleDateFormat.get().clone(); } public int hashCode() { return localSimpleDateFormat.get().hashCode(); } public boolean equals(Object obj) { return localSimpleDateFormat.get().equals(obj); } } 

https://gist.github.com/pablomoretti/9748230

4 Comments

I have serious doubts if the overhead of thread lookup and synchronization is not bigger than the cost of creating a new instance each time
@JakubBochenski Here is a post that list the comparison of different approaches. It looks like ThreadLocal approach yields the best performance. javacodegeeks.com/2010/07/…
@DavidRuan thanks, but to quote the top comment on that article: Could u please provide the source code and the testing code?. Not knowing if it was benchmarked properly it's just a random graph on the internet.
Problem with this solution is it allows to manipulate the SimpleDateFormat which can result in a strange state! This is inconsistent and not thread safe. If the SimpleDateFormat was immutable this solution woubld be smart - gist.github.com/pablomoretti/9748230#gistcomment-3758032
17

Release 3.2 of commons-lang will have FastDateParser class that is a thread-safe substitute of SimpleDateFormat for Gregorian calendar. See LANG-909 for more information.

Comments

11

Here is the example which results in a strange error. Even Google gives no results:

public class ExampleClass { private static final Pattern dateCreateP = Pattern.compile("Дата подачи:\\s*(.+)"); private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy"); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(100); while (true) { executor.submit(new Runnable() { @Override public void run() { workConcurrently(); } }); } } public static void workConcurrently() { Matcher matcher = dateCreateP.matcher("Дата подачи: 19:30:55 03.05.2015"); Timestamp startAdvDate = null; try { if (matcher.find()) { String dateCreate = matcher.group(1); startAdvDate = new Timestamp(sdf.parse(dateCreate).getTime()); } } catch (Throwable th) { th.printStackTrace(); } System.out.print("OK "); } } 

And result :

OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK java.lang.NumberFormatException: For input string: ".201519E.2015192E2" at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043) at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.lang.Double.parseDouble(Double.java:538) at java.text.DigitList.getDouble(DigitList.java:169) at java.text.DecimalFormat.parse(DecimalFormat.java:2056) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at com.nonscalper.webscraper.processor.av.ExampleClass.workConcurrently(ExampleClass.java:37) at com.nonscalper.webscraper.processor.av.ExampleClass$1.run(ExampleClass.java:25) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) 

1 Comment

See sgokhales' answer higher in the thread. Follow those guidelines to get a thread-safe simpledateformat.
6

Here’s an example defines a SimpleDateFormat object as a static field. When two or more threads access “someMethod” concurrently with different dates, they can mess with each other’s results.

 public class SimpleDateFormatExample { private static final SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); public String someMethod(Date date) { return simpleFormat.format(date); } } 

You can create a service like below and use jmeter to simulate concurrent users using the same SimpleDateFormat object formatting different dates and their results will be messed up.

public class FormattedTimeHandler extends AbstractHandler { private static final String OUTPUT_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; private static final String INPUT_TIME_FORMAT = "yyyy-MM-ddHH:mm:ss"; private static final SimpleDateFormat simpleFormat = new SimpleDateFormat(OUTPUT_TIME_FORMAT); // apache commons lang3 FastDateFormat is threadsafe private static final FastDateFormat fastFormat = FastDateFormat.getInstance(OUTPUT_TIME_FORMAT); public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); baseRequest.setHandled(true); final String inputTime = request.getParameter("time"); Date date = LocalDateTime.parse(inputTime, DateTimeFormat.forPattern(INPUT_TIME_FORMAT)).toDate(); final String method = request.getParameter("method"); if ("SimpleDateFormat".equalsIgnoreCase(method)) { // use SimpleDateFormat as a static constant field, not thread safe response.getWriter().println(simpleFormat.format(date)); } else if ("FastDateFormat".equalsIgnoreCase(method)) { // use apache commons lang3 FastDateFormat, thread safe response.getWriter().println(fastFormat.format(date)); } else { // create new SimpleDateFormat instance when formatting date, thread safe response.getWriter().println(new SimpleDateFormat(OUTPUT_TIME_FORMAT).format(date)); } } public static void main(String[] args) throws Exception { // embedded jetty configuration, running on port 8090. change it as needed. Server server = new Server(8090); server.setHandler(new FormattedTimeHandler()); server.start(); server.join(); } 

}

The code and jmeter script can be downloaded here .

Comments

3

Here is a code example that proves the fault in the class. I've checked: the problem occurs when using parse and also when you are only using format.

3 Comments

This code sample has few flaws: NumberFormatException / ArrayIndexOutOfBoundsException can also be thrown due to concurrency issue and they "silently" kill the thread. Also threads are not joined, which is not good. Check classes in LANG-909 – I think they look better.
@dma_k I don't quite see why you would join threads in test code whose sole purpose it is to fail and die. :-) Anyway: I didn't want to recommend the ThreadSafeSimpleDateFormat from the blog (you are right: there are much better solutions) but to point to the failure demonstration.
This is more important for Unix tests, where died threads will not impact the result of test itself. Yes, something will be printed to console, but from exception one cannot recognize is it due to error in program (format / input data) or concurrency issue. The code is fine by itself, my comment is for those who will copy/paste it and use under different conditions.
-5

If you want to use the same date format among multiple threads, declare it as a static and synchronize on the instance variable when using it...

static private SimpleDateFormat sdf = new SimpleDateFormat("...."); synchronized(sdf) { // use the instance here to format a date } // The above makes it thread safe 

3 Comments

But surely the time wasted aquiring the sdf's monitor will be greater than just creating a new one each time?
The slowest operation you can perform in java is a call to new.
+ you will kick the code execution on the synchronized block to a single thread pipe.. good job (irony)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.