diff --git a/jdk/src/share/classes/java/text/DateFormatSymbols.java b/jdk/src/share/classes/java/text/DateFormatSymbols.java index 4d6f08f4cac..2eeb07e3447 100644 --- a/jdk/src/share/classes/java/text/DateFormatSymbols.java +++ b/jdk/src/share/classes/java/text/DateFormatSymbols.java @@ -44,11 +44,12 @@ import java.io.Serializable; 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 @@ public class DateFormatSymbols implements Serializable, Cloneable { * @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 ref = cachedInstances.get(locale); + DateFormatSymbols dfs = null; + if (ref == null || (dfs = ref.get()) == null) { + dfs = new DateFormatSymbols(locale); + ref = new SoftReference(dfs); + SoftReference 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 @@ public class DateFormatSymbols implements Serializable, Cloneable { static final int millisPerHour = 60*60*1000; /** - * Cache to hold the FormatData and TimeZoneNames ResourceBundles - * of a Locale. + * Cache to hold DateFormatSymbols instances per Locale. */ - private static Hashtable cachedLocaleData = new Hashtable(3); - - /** - * Look up resource data for the desiredLocale in the cache; update the - * cache if necessary. - */ - 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> cachedInstances + = new ConcurrentHashMap>(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 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 cachedLocaleData + = new ConcurrentHashMap(3); } diff --git a/jdk/src/share/classes/java/text/SimpleDateFormat.java b/jdk/src/share/classes/java/text/SimpleDateFormat.java index defd076ed21..5f1c6601099 100644 --- a/jdk/src/share/classes/java/text/SimpleDateFormat.java +++ b/jdk/src/share/classes/java/text/SimpleDateFormat.java @@ -44,13 +44,14 @@ import java.io.ObjectInputStream; 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 @@ public class SimpleDateFormat extends DateFormat { /** * Cache to hold the DateTimePatterns of a Locale. */ - private static Hashtable cachedLocaleData - = new Hashtable(3); + private static final ConcurrentMap cachedLocaleData + = new ConcurrentHashMap(3); /** * Cache NumberFormat instances with Locale key. */ - private static Hashtable cachedNumberFormatData - = new Hashtable(3); + private static final ConcurrentMap cachedNumberFormatData + = new ConcurrentHashMap(3); /** * The Locale used to instantiate this @@ -579,7 +580,7 @@ public class SimpleDateFormat extends DateFormat { initializeCalendar(locale); this.pattern = pattern; - this.formatData = DateFormatSymbols.getInstance(locale); + this.formatData = DateFormatSymbols.getInstanceRef(locale); this.locale = locale; initialize(locale); } @@ -632,9 +633,9 @@ public class SimpleDateFormat extends DateFormat { 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 @@ public class SimpleDateFormat extends DateFormat { numberFormat.setGroupingUsed(false); /* update cache */ - cachedNumberFormatData.put(loc, numberFormat); + cachedNumberFormatData.putIfAbsent(loc, numberFormat); } numberFormat = (NumberFormat) numberFormat.clone(); @@ -897,7 +898,7 @@ public class SimpleDateFormat extends DateFormat { * 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 @@ public class SimpleDateFormat extends DateFormat { * @since 1.2 */ public void set2DigitYearStart(Date startDate) { - parseAmbiguousDatesAsAfter(startDate); + parseAmbiguousDatesAsAfter(new Date(startDate.getTime())); } /** @@ -934,7 +935,7 @@ public class SimpleDateFormat extends DateFormat { * @since 1.2 */ public Date get2DigitYearStart() { - return defaultCenturyStart; + return (Date) defaultCenturyStart.clone(); } /** diff --git a/jdk/src/share/classes/java/util/Calendar.java b/jdk/src/share/classes/java/util/Calendar.java index 7ec5e87ec39..70460eaec07 100644 --- a/jdk/src/share/classes/java/util/Calendar.java +++ b/jdk/src/share/classes/java/util/Calendar.java @@ -51,6 +51,8 @@ import java.security.PrivilegedExceptionAction; 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 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable cachedLocaleData = new Hashtable(3); + private static final ConcurrentMap cachedLocaleData + = new ConcurrentHashMap(3); // Special values of stamp[] /** @@ -1022,7 +1025,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable