3

Using a pattern very similar to that described in a recent question, for a multithreaded application, I am getting weird date values (e.g., years like 2025 or 2035, when clearly no such value exists in the source data). It seems that a concurrency issue is occuring.

The source code looks something like

// Various Java DateFormat patterns, e.g. "yyyy-MM-dd". private static final String[] DATE_PATTERNS = new String[] {...}; private static SimpleDateFormat[] getFormats(final String[] patterns) { ThreadLocal<SimpleDateFormat[]> LOCAL_FORMATS = new ThreadLocal<SimpleDateFormat[]>() { @Override protected SimpleDateFormat[] initialValue() { List<SimpleDateFormat> formatList = new ArrayList<SimpleDateFormat>(); for (String pattern:patterns) { formatList.add(new SimpleDateFormat(pattern)); } return formatList.toArray(new SimpleDateFormat[formatList.size()]); } }; return LOCAL_FORMATS.get(); // Create a thread-local copy } private static final SimpleDateFormat[] DATE_FORMATS = getFormats(DATE_PATTERNS); 

After its static initialization, the DATE_FORMATS array is accessed by numerous classes, which in turn use the SimpleDateFormat objects of the array for parsing or formatting several date strings.

Can there be any concurrency issue in such a usage scenario, especially given the use of ThreadLocal?

2 Answers 2

4

Yes, there can be concurrency issues. Your thread local variable doesn't serve any purpose. It's only used when the class is initialized, to temporarily store an array of date formats that is immediately retrieved and stored in a static constant.

All the threads, after, always use the same instances of date formats concurrently, without getting them from any thread local variable.

The code should rather be:

private static final String[] DATE_PATTERNS = new String[] {...}; private static final ThreadLocal<SimpleDateFormat[]> DATE_FORMATS = new ThreadLocal<SimpleDateFormat[]>() { @Override protected SimpleDateFormat[] initialValue() { List<SimpleDateFormat> formatList = new ArrayList<SimpleDateFormat>(); for (String pattern : DATE_PATTERNS) { formatList.add(new SimpleDateFormat(pattern)); } return formatList.toArray(new SimpleDateFormat[formatList.size()]); } }; public static SimpleDateFormat[] getDateFormats() { return DATE_FORMATS.get(); } 

I would also use an unmodifiable List<SimpleDateFormat> rather than an array, to be safer.

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

3 Comments

I was actually asking whether the solution you suggest would be the correct one while you were editing your answer. Thanks for the details! So, each method calling getDateFormats() will always get the same SimpleDateFormat[] array for the same thread, while for each new thread that array will only be initialized once, correct?
Instead of getDateFormats() one could use DATE_FORMATS.get(), of course. If the SimpleDateFormat[] array never gets written, it should be thread-safe. Thanks again for the answers!
Even if written, it will be thread-safe, since each thread has its own copy. But if you want to prevent an accidental reordering or nullification of the array members, you'd better make the array an unmodifiable list.
2
// Various Java DateFormat patterns, e.g. "yyyy-mm-dd". 

The format 'yyyy-mm-dd' is likely to give you weird results because 'mm' is minutes and not months. From the javadoc:

M Month in year Month July; Jul; 07 ... m Minute in hour Number 30 

1 Comment

Not actually used, just a typo in the comment above. Amended, thanks for spotting it! :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.