patternjavaMinor
Helper class to memoize DateFormats application-wide
Viewed 0 times
dateformatsapplicationhelperwideclassmemoize
Problem
Premise
Consider the following method:
It's often desirable to memoize
But this is wrong for multithreaded applications. From the
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.
Assuming our application is using thread pooling, this leads us to memoize an object per thread, using
This works fine. But I've noticed it can still leave code duplication across a larger project. For example multiple developers could each write their own code using
Solution 1
```
public class DateFormats {
private static final ThreadLocal> DATE_FORMAT_MAP_REF =
new ThreadLocal>() {
@Override
protected Map initialValue() {
return Maps.newHashMap();
}
};
private DateFormats() { }
/**
* Retrieves, and if necessary creates and caches, a {@code DateFormat} instance
* corresponding to the specified for
Consider the following method:
static String formatMyDate(Date date) {
return new SimpleDateFormat("yyyy-MM-dd").format(date);
}It's often desirable to memoize
DateFormat objects so they can be reused rather than repeatedly instantiating new ones. This frequently leads to the following naive refactor:private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
static String formatMyDate(Date date) {
return DATE_FORMAT.format(date);
}But this is wrong for multithreaded applications. From the
DateFormat documentation: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.
Assuming our application is using thread pooling, this leads us to memoize an object per thread, using
ThreadLocal (see article):private static final ThreadLocal DATE_FORMAT_REF =
new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
static String formatMyDate(Date date) {
return DATE_FORMAT_REF.get().format(date);
}This works fine. But I've noticed it can still leave code duplication across a larger project. For example multiple developers could each write their own code using
"yyyy-MM-dd" date formats in various classes. This led me to write the following helper class (using JSR-305 and Guava):Solution 1
```
public class DateFormats {
private static final ThreadLocal> DATE_FORMAT_MAP_REF =
new ThreadLocal>() {
@Override
protected Map initialValue() {
return Maps.newHashMap();
}
};
private DateFormats() { }
/**
* Retrieves, and if necessary creates and caches, a {@code DateFormat} instance
* corresponding to the specified for
Solution
It looks like the questions are very specific. You should probably formulate a goal. Is your main goal to reduce code duplicity? Memory usage? Speed (however we define it here)? Something else? A combination of previous items? (which makes life harder the more items you combine)
This affects the first two questions.
For the first question, I could imagine, that you have some common date formats. If less than 5 formats make up to 90% of usage, I would supply a specific method like
You could use
This would solve this the easiest way. If you need any more sophisticated solution, you could change the class internals without breaking any external code.
For the last 2 questions, I do not have experience with
I would have used a standard HashMap to store the ThreadLocal Version of DateFormat. As far as I thought about it, we do not need synchronization for the map, because we do not want to replace entries, we do not care if we put something multiple times in rare cases for the initialization part of a new datestring and we do not remove anything.
A bit offtopic, but: One thing I do not get about your description. If they do not care about using yoda (which could be used inside this DateFormats class and no one would notice about it), how could you convince them to use this DateFormats class?
This affects the first two questions.
For the first question, I could imagine, that you have some common date formats. If less than 5 formats make up to 90% of usage, I would supply a specific method like
/**
* Formats the date exactly the same as SimpleDateFormat("yyyy-MM-dd").format(date),
* but is multi threaded safe without any penalties in subsequent calls.
* Example: 2012-09-30
*/
public static String (DateFormats.)formatDate_yyyy_MM_dd(Date date) { ... }You could use
ThreadLocal then.This would solve this the easiest way. If you need any more sophisticated solution, you could change the class internals without breaking any external code.
For the last 2 questions, I do not have experience with
LoadingCache, so I cannot help there.I would have used a standard HashMap to store the ThreadLocal Version of DateFormat. As far as I thought about it, we do not need synchronization for the map, because we do not want to replace entries, we do not care if we put something multiple times in rare cases for the initialization part of a new datestring and we do not remove anything.
A bit offtopic, but: One thing I do not get about your description. If they do not care about using yoda (which could be used inside this DateFormats class and no one would notice about it), how could you convince them to use this DateFormats class?
Code Snippets
/**
* Formats the date exactly the same as SimpleDateFormat("yyyy-MM-dd").format(date),
* but is multi threaded safe without any penalties in subsequent calls.<br>
* Example: 2012-09-30
*/
public static String (DateFormats.)formatDate_yyyy_MM_dd(Date date) { ... }Context
StackExchange Code Review Q#22867, answer score: 3
Revisions (0)
No revisions yet.