6991380: (cal) Calendar.cachedLocaleData should be transitioned from Hashtable to ConcurrentHashMap
6560965: [Fmt-Da] defaultCenturyStart In SimpleDateFormat should be protected
6560980: [Fmt-Da] DateFormatSymbols.cacheLookup doesn't update cache correctly.
Reviewed-by: naoto, peytoia
--- a/jdk/src/share/classes/java/text/DateFormatSymbols.java Mon Oct 18 14:45:00 2010 -0700
+++ b/jdk/src/share/classes/java/text/DateFormatSymbols.java Wed Oct 20 14:41:39 2010 +0900
@@ -44,11 +44,12 @@
import java.lang.ref.SoftReference;
import java.text.spi.DateFormatSymbolsProvider;
import java.util.Arrays;
-import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.spi.LocaleServiceProvider;
import sun.util.LocaleServiceProviderPool;
import sun.util.TimeZoneNameUtility;
@@ -321,20 +322,64 @@
* @since 1.6
*/
public static final DateFormatSymbols getInstance(Locale locale) {
+ DateFormatSymbols dfs = getProviderInstance(locale);
+ if (dfs != null) {
+ return dfs;
+ }
+ return (DateFormatSymbols) getCachedInstance(locale).clone();
+ }
+
+ /**
+ * Returns a DateFormatSymbols provided by a provider or found in
+ * the cache. Note that this method returns a cached instance,
+ * not its clone. Therefore, the instance should never be given to
+ * an application.
+ */
+ static final DateFormatSymbols getInstanceRef(Locale locale) {
+ DateFormatSymbols dfs = getProviderInstance(locale);
+ if (dfs != null) {
+ return dfs;
+ }
+ return getCachedInstance(locale);
+ }
+
+ private static DateFormatSymbols getProviderInstance(Locale locale) {
+ DateFormatSymbols providersInstance = null;
// Check whether a provider can provide an implementation that's closer
// to the requested locale than what the Java runtime itself can provide.
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
if (pool.hasProviders()) {
- DateFormatSymbols providersInstance = pool.getLocalizedObject(
- DateFormatSymbolsGetter.INSTANCE, locale);
- if (providersInstance != null) {
- return providersInstance;
+ providersInstance = pool.getLocalizedObject(
+ DateFormatSymbolsGetter.INSTANCE, locale);
+ }
+ return providersInstance;
+ }
+
+ /**
+ * Returns a cached DateFormatSymbols if it's found in the
+ * cache. Otherwise, this method returns a newly cached instance
+ * for the given locale.
+ */
+ private static DateFormatSymbols getCachedInstance(Locale locale) {
+ SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
+ DateFormatSymbols dfs = null;
+ if (ref == null || (dfs = ref.get()) == null) {
+ dfs = new DateFormatSymbols(locale);
+ ref = new SoftReference<DateFormatSymbols>(dfs);
+ SoftReference<DateFormatSymbols> x = cachedInstances.putIfAbsent(locale, ref);
+ if (x != null) {
+ DateFormatSymbols y = x.get();
+ if (y != null) {
+ dfs = y;
+ } else {
+ // Replace the empty SoftReference with ref.
+ cachedInstances.put(locale, ref);
+ }
}
}
-
- return new DateFormatSymbols(locale);
+ return dfs;
}
/**
@@ -597,56 +642,44 @@
static final int millisPerHour = 60*60*1000;
/**
- * Cache to hold the FormatData and TimeZoneNames ResourceBundles
- * of a Locale.
- */
- private static Hashtable cachedLocaleData = new Hashtable(3);
-
- /**
- * Look up resource data for the desiredLocale in the cache; update the
- * cache if necessary.
+ * Cache to hold DateFormatSymbols instances per Locale.
*/
- private static ResourceBundle cacheLookup(Locale desiredLocale) {
- ResourceBundle rb;
- SoftReference data
- = (SoftReference)cachedLocaleData.get(desiredLocale);
- if (data == null) {
- rb = LocaleData.getDateFormatData(desiredLocale);
- data = new SoftReference(rb);
- cachedLocaleData.put(desiredLocale, data);
- } else {
- if ((rb = (ResourceBundle)data.get()) == null) {
- rb = LocaleData.getDateFormatData(desiredLocale);
- data = new SoftReference(rb);
- }
- }
- return rb;
- }
+ private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
+ = new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(3);
private void initializeData(Locale desiredLocale) {
- int i;
- ResourceBundle resource = cacheLookup(desiredLocale);
+ locale = desiredLocale;
- // FIXME: cache only ResourceBundle. Hence every time, will do
- // getObject(). This won't be necessary if the Resource itself
- // is cached.
- eras = (String[])resource.getObject("Eras");
+ // Copy values of a cached instance if any.
+ SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
+ DateFormatSymbols dfs;
+ if (ref != null && (dfs = ref.get()) != null) {
+ copyMembers(dfs, this);
+ return;
+ }
+
+ // Initialize the fields from the ResourceBundle for locale.
+ ResourceBundle resource = LocaleData.getDateFormatData(locale);
+
+ eras = resource.getStringArray("Eras");
months = resource.getStringArray("MonthNames");
shortMonths = resource.getStringArray("MonthAbbreviations");
- String[] lWeekdays = resource.getStringArray("DayNames");
- weekdays = new String[8];
- weekdays[0] = ""; // 1-based
- for (i=0; i<lWeekdays.length; i++)
- weekdays[i+1] = lWeekdays[i];
- String[] sWeekdays = resource.getStringArray("DayAbbreviations");
- shortWeekdays = new String[8];
- shortWeekdays[0] = ""; // 1-based
- for (i=0; i<sWeekdays.length; i++)
- shortWeekdays[i+1] = sWeekdays[i];
ampms = resource.getStringArray("AmPmMarkers");
localPatternChars = resource.getString("DateTimePatternChars");
- locale = desiredLocale;
+ // Day of week names are stored in a 1-based array.
+ weekdays = toOneBasedArray(resource.getStringArray("DayNames"));
+ shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations"));
+ }
+
+ private static String[] toOneBasedArray(String[] src) {
+ int len = src.length;
+ String[] dst = new String[len + 1];
+ dst[0] = "";
+ for (int i = 0; i < len; i++) {
+ dst[i + 1] = src[i];
+ }
+ return dst;
}
/**
--- a/jdk/src/share/classes/java/text/DecimalFormat.java Mon Oct 18 14:45:00 2010 -0700
+++ b/jdk/src/share/classes/java/text/DecimalFormat.java Wed Oct 20 14:41:39 2010 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -46,9 +46,10 @@
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Currency;
-import java.util.Hashtable;
import java.util.Locale;
import java.util.ResourceBundle;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import sun.util.resources.LocaleData;
@@ -394,14 +395,14 @@
public DecimalFormat() {
Locale def = Locale.getDefault(Locale.Category.FORMAT);
// try to get the pattern from the cache
- String pattern = (String) cachedLocaleData.get(def);
+ String pattern = cachedLocaleData.get(def);
if (pattern == null) { /* cache miss */
// Get the pattern for the default locale.
ResourceBundle rb = LocaleData.getNumberFormatData(def);
String[] all = rb.getStringArray("NumberPatterns");
pattern = all[0];
/* update cache */
- cachedLocaleData.put(def, pattern);
+ cachedLocaleData.putIfAbsent(def, pattern);
}
// Always applyPattern after the symbols are set
@@ -3272,5 +3273,6 @@
/**
* Cache to hold the NumberPattern of a Locale.
*/
- private static Hashtable cachedLocaleData = new Hashtable(3);
+ private static final ConcurrentMap<Locale, String> cachedLocaleData
+ = new ConcurrentHashMap<Locale, String>(3);
}
--- a/jdk/src/share/classes/java/text/SimpleDateFormat.java Mon Oct 18 14:45:00 2010 -0700
+++ b/jdk/src/share/classes/java/text/SimpleDateFormat.java Wed Oct 20 14:41:39 2010 +0900
@@ -44,13 +44,14 @@
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
-import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import sun.util.calendar.CalendarUtils;
import sun.util.calendar.ZoneInfoFile;
import sun.util.resources.LocaleData;
@@ -503,14 +504,14 @@
/**
* Cache to hold the DateTimePatterns of a Locale.
*/
- private static Hashtable<String,String[]> cachedLocaleData
- = new Hashtable<String,String[]>(3);
+ private static final ConcurrentMap<String, String[]> cachedLocaleData
+ = new ConcurrentHashMap<String, String[]>(3);
/**
* Cache NumberFormat instances with Locale key.
*/
- private static Hashtable<Locale,NumberFormat> cachedNumberFormatData
- = new Hashtable<Locale,NumberFormat>(3);
+ private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
+ = new ConcurrentHashMap<Locale, NumberFormat>(3);
/**
* The Locale used to instantiate this
@@ -579,7 +580,7 @@
initializeCalendar(locale);
this.pattern = pattern;
- this.formatData = DateFormatSymbols.getInstance(locale);
+ this.formatData = DateFormatSymbols.getInstanceRef(locale);
this.locale = locale;
initialize(locale);
}
@@ -632,9 +633,9 @@
dateTimePatterns = r.getStringArray("DateTimePatterns");
}
/* update cache */
- cachedLocaleData.put(key, dateTimePatterns);
+ cachedLocaleData.putIfAbsent(key, dateTimePatterns);
}
- formatData = DateFormatSymbols.getInstance(loc);
+ formatData = DateFormatSymbols.getInstanceRef(loc);
if ((timeStyle >= 0) && (dateStyle >= 0)) {
Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
dateTimePatterns[dateStyle + 4]};
@@ -665,7 +666,7 @@
numberFormat.setGroupingUsed(false);
/* update cache */
- cachedNumberFormatData.put(loc, numberFormat);
+ cachedNumberFormatData.putIfAbsent(loc, numberFormat);
}
numberFormat = (NumberFormat) numberFormat.clone();
@@ -897,7 +898,7 @@
* so we can call it from readObject().
*/
private void initializeDefaultCentury() {
- calendar.setTime( new Date() );
+ calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add( Calendar.YEAR, -80 );
parseAmbiguousDatesAsAfter(calendar.getTime());
}
@@ -921,7 +922,7 @@
* @since 1.2
*/
public void set2DigitYearStart(Date startDate) {
- parseAmbiguousDatesAsAfter(startDate);
+ parseAmbiguousDatesAsAfter(new Date(startDate.getTime()));
}
/**
@@ -934,7 +935,7 @@
* @since 1.2
*/
public Date get2DigitYearStart() {
- return defaultCenturyStart;
+ return (Date) defaultCenturyStart.clone();
}
/**
--- a/jdk/src/share/classes/java/util/Calendar.java Mon Oct 18 14:45:00 2010 -0700
+++ b/jdk/src/share/classes/java/util/Calendar.java Wed Oct 20 14:41:39 2010 +0900
@@ -51,6 +51,8 @@
import java.security.ProtectionDomain;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import sun.util.BuddhistCalendar;
import sun.util.calendar.ZoneInfo;
import sun.util.resources.LocaleData;
@@ -837,7 +839,8 @@
* Cache to hold the firstDayOfWeek and minimalDaysInFirstWeek
* of a Locale.
*/
- private static Hashtable<Locale, int[]> cachedLocaleData = new Hashtable<Locale, int[]>(3);
+ private static final ConcurrentMap<Locale, int[]> cachedLocaleData
+ = new ConcurrentHashMap<Locale, int[]>(3);
// Special values of stamp[]
/**
@@ -1022,7 +1025,7 @@
// returns a BuddhistCalendar instance.
if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) {
- cal = new BuddhistCalendar(zone, aLocale);
+ cal = new BuddhistCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
@@ -2588,7 +2591,7 @@
data = new int[2];
data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek"));
data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek"));
- cachedLocaleData.put(desiredLocale, data);
+ cachedLocaleData.putIfAbsent(desiredLocale, data);
}
firstDayOfWeek = data[0];
minimalDaysInFirstWeek = data[1];
--- a/jdk/src/share/classes/java/util/TimeZone.java Mon Oct 18 14:45:00 2010 -0700
+++ b/jdk/src/share/classes/java/util/TimeZone.java Wed Oct 20 14:41:39 2010 +0900
@@ -160,11 +160,6 @@
private static final int ONE_HOUR = 60*ONE_MINUTE;
private static final int ONE_DAY = 24*ONE_HOUR;
- /**
- * Cache to hold the SimpleDateFormat objects for a Locale.
- */
- private static Hashtable cachedLocaleData = new Hashtable(3);
-
// Proclaim serialization compatibility with JDK 1.1
static final long serialVersionUID = 3581463369166924961L;