Reputation: 10084
I have a database with many tables that store date/times in a UTC numeric format (modified Julian Day numbers).
This works well, but of course any time dates need to be displayed to the user, the date/time must be converted from a UTC numeric format to a localized String format. Because the JDK Calendar API provides for historically accurate daylight savings and time zone offsets, I have been using Gregorian Calendar for this sole purpose. I use it only to map timestamps from UTC to local time.
Because I have so many tables that need to perform this conversion and because Calendar objects are expensive to create, I have been creating new GregorianCalendar objects only when the locale time zone changes. I've been keeping the current calendar in a static member field in the JulianDay class (this is the class that provides the functions I use to provide the UTC to local mappings). Here are the fields of JulianDay:
public final class JulianDay {
private static final int YEAR = 0;
private static final int MONTH = 1;
private static final int DAY = 2;
private static final int HOURS = 3;
private static final int MINUTES = 4;
private static final int SECONDS = 5;
private static final int MILLIS = 6;
public static final double POSIX_EPOCH_MJD = 40587.0;
private static final String TIME_ZONE_UTC = "UTC";
private static final GregorianCalendar CAL_UTC =
new GregorianCalendar(TimeZone.getTimeZone(TIME_ZONE_UTC));
private static GregorianCalendar c_selCal = null;
public static void setCurrentCalendar(String tzid)
{ JulianDay.c_selCal = JulianDay.findCalendarForTimeZone(tzid); }
public static GregorianCalendar getCurrentCalendar()
{ return JulianDay.c_selCal; }
:
:
}
The above is in a single-threaded client-side GUI application, but I will soon need to develop a server-side web application that will use much of this API. I'll need to figure out how to preserve my investment in the JDK Calendar API and somehow keep my application thread safe. Joda time is an option, but if possible I'd like to not add that dependency (all I use the Java API for anyway is the easymode access to the time zone database).
Since Calendar is patently thread hostile, I'm not exactly sure how to go from here.
My thought is that since the JulianDay class constructs all the instances of GregorianCalendar (from a time zone id string), I can refactor my code so that I can eliminate the getCurrentCalendar()
method which publishes the reference of the calendar.
If I can confine all the access to GregorianCalendar
to within JulianDay
, may I safely assume that, even though it's a dangerously mutable class, my application is thread safe? I will still need to synchronize access to the GregorianCalendar
with a locking object even in my JulianDay
class, is that right?
Upvotes: 1
Views: 1754
Reputation: 10084
How about this as a solution?:
Currently, JulianDay is a class with only static members (all the methods are static) and I make no instances of JulianDay.
I propose instead to refactor JulianDay like this:
I believe that instances of JulianDay would be Thread Safe if developed as above, and the best part is that I would not need to do much refactoring of my existing code to get it to work with instantiable JulianDay instances.
The key part will be not letting GregorianCalendar escape.
Upvotes: 0
Reputation: 13164
A mutable instance can be thread-safe if properly synchronized. The advantage of an immutable instances is just that you don't need to synchronize. So yes, if you synchronize all mutating changes to the Calendar you can keep using it.
But! Server-side apps are totally different beasts than even a multi-threaded client app. A single point of synchronization is a sure way to block the app - depending on the load the connections may stall on it. While a ThreadLocal might mitigate the synchronization problem, since each Calendar is only visible within a single thread, think about the thread reuse.
In a typical app server environment the same thread will be reused not only for consecutive users but also within the same session - to control and preserve server resources each GET is a work unit that a single thread get to serve it, as soon as the work unit is finished, the same thread can get another work unit for another user. So if calendars were mutable and had to be changed depending on a particular user you would end up having to set them up each time they are needed. So practically no caching effect at all!
For services like that (a service of converting dates) in app server environment there were (I say "were" since that whas what we used to do some years ago) stateless and stateful beans, now I think a cache service could be more appropriate, filled with several instances per time zone.
Still as long as the instances are mutable they would have to be locked (removed and reinserted perhaps) in the cache. And the cache must be managed, appropriately sized and the like.
Best is always to remain stateless - like with Joda. And prefilled cache to refrain from dynamically creating new objects (depending on the scale another method to slow app server down) on per record.
Upvotes: 1
Reputation: 13967
You coud use a Thread local, static member variable, e.g.:
private static final ThreadLocal<GregorianCalendar> CAL_UTC =
new ThreadLocal<GregorianCalendar>() {
@Override protected GregorianCalendar initialValue() {
return new GregorianCalendar(TimeZone.getTimeZone(TIME_ZONE_UTC));
}
};
This way you have an object per thread. Check the ORACLE documentation for more.
Hope this helps ... Cheers!
p.s.: Using this approach there are two aspects to consider:
ThreadLocal
instance.If you need to access the instance accross threads you've to derive your own class and synchronize relevant methods or look for alternative implementations (check for instance Apache commons).
Upvotes: 3