Reputation: 6941
I'd like to use a Calendar for some static methods and use a static field:
private static Calendar calendar = Calendar.getInstance();
Now I read java.util.Calendar isn't thread safe. How can I make this thread safe (it should be static)?
Upvotes: 10
Views: 12997
Reputation: 18963
Instead of trying to make the Calendar
API thread-safe, use the thread-safe java.time
API introduced in Java 8 instead. This API also provides a more fluent and intuitive interface for date and time manipulation.
Here's an example compatible with the legacy Date
API of getting the start of next day with the java.time
API:
import java.time.LocalDateTime;
import java.time.ZoneId;
public static Date getStartOfNextDay() {
final ZonedDateTime startOfNextDay = ZonedDateTime.now()
.plusDays(1)
.withHour(0).withMinute(0).withSecond(0).withNano(0);
return Date.from(startOfNextDay.toInstant());
}
And here's how it looks with the Calendar
API:
import java.util.Calendar;
public static Date getStartOfNextDay() {
final Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
Upvotes: 2
Reputation: 339332
Calendar
replaced by ZonedDateTime
. Immutable, so thread-safe.
private static ZonedDateTime someMoment = ZonedDateTime.now(); // Capture the current moment as seen in the JVM’s current default time zone.
If the value is meant to be fixed, unchanging during the execution of your app, mark it final
.
private static final ZonedDateTime someMoment = ZonedDateTime.now();
If at runtime you might assign a different object, then for thread-safety use an AtomicReference
.
private static final AtomicReference< ZonedDateTime > someMomentRef = new AtomicReference<>( ZonedDateTime.now() ) ;
Later updating with a fresh moment.
someMomentRef.set( ZonedDateTime.now() );
If you need to interoperate with old code not yet updated to java.time, convert.
ZonedDateTime someMoment = someMomentRef.get() ;
Calendar c = GregorianCalendar.from( someMoment ) ; // `GregorianCalendar` is a concrete subclass of `Calendar`.
java.time.ZonedDateTime
The terribly flawed java.util.Calendar
class has been supplanted by the modern java.time classes defined in JSR 310. Specifically replaced by java.time.ZonedDateTime
.
A ZonedDateTime
object represents a date with time-of-day as seen in a particular time zone.
Understand that a time zone is a named history of the changes to the offset used by the people of a particular region as decided by their politicians. An offset is merely a number of hours-minutes-seconds ahead or behind the temporal meridian of UTC.
To get the current moment, specify your desired/expected time zone.
ZoneId z = ZoneId.of( "Africa/Casablanca" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
If you want the JVM’s current default time zone, use ZoneId.systemDefault()
.
You can store a ZonedDateTime
object in a static
reference. But be aware that such an object stores a frozen moment, is immutable, and does not adjust to later moments. If you want a later moment, call now
method ago to generate a new fresh ZonedDateTime
object.
private static final ZonedDateTime appLaunched = ZonedDateTime.now( ZoneId.of( "Asia/Tokyo" ) );
java.time.Instant
If you merely want to track a moment, a specific point on the timeline, without business rules explicitly aimed at time zones, use a moment as seen in UTC. For that, use java.time.Instant
class.
private static final Instant instant = Instant.now() ; // Always in UTC, an offset of zero.
You can later apply a time zone for presentation to the user.
ZonedDateTime zdt = instant.atZone( ZoneId.systemDefault() ) ;
String output =
zdt
.atZone( ZoneId.systemDefault() ) // Returns a `ZonedDateTime` object.
.format(
DateTimeFormatter
.ofLocalizedDateTime( FormatStyle.FULL ) // Returns a `DateTimeFormatter` object.
.withLocale( Locale.getDefault() )
) ; // Returns a `String` object.
Upvotes: 3
Reputation: 19340
Create a Calendar
as a local variable in the method. If you need the same calendar across methods, you may be using statics where a (singleton or quasi-singleton) object would be more appropriate.
Upvotes: -1
Reputation: 533660
Calendar is thread safe provided you don't change it. The usage in your example is fine.
It is worth noting that Calendar is not an efficient class and you should only use it for complex operations (like finding the next month/year) IMHO: If you do use it for complex operations, use local variables only.
If all you want it a snapshot of the time a faster way is to use currentTimeMillis which does even create an object. You can make the field volatile if you want to make it thread safe.
private static long now = System.currentTimeMillis();
The usage is a bit suspect. Why would you get the current time and store it globally like this. It reminds me of the old joke.
- Do you have the time?
- Yes, I have it written down somewhere.
Upvotes: 3
Reputation: 1502016
You can't make something thread-safe if it isn't. In the case of Calendar
, even reading data from it isn't thread-safe, as it can update internal data structures.
If at all possible, I'd suggest using Joda Time instead:
If you absolutely have to use a Calendar
, you could create a locking object and put all the access through a lock. For example:
private static final Calendar calendar = Calendar.getInstance();
private static final Object calendarLock = new Object();
public static int getYear()
{
synchronized(calendarLock)
{
return calendar.get(Calendar.YEAR);
}
}
// Ditto for other methods
It's pretty nasty though. You could have just one synchronized method which created a clone of the original calendar each time it was needed, of course... it's possible that by calling computeFields
or computeTime
you could make subsequent read-operations thread-safe, of course, but personally I'd be loathe to try it.
Upvotes: 14
Reputation: 9399
You cannot. Yes, you could synchronize on it, but it still has mutable state fields. You'll have to create your own Calendar object.
If possible, use something lightweight, like a long measuring the times in milliseconds, and only convert to a Calendar when you NEED to.
Upvotes: 2