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
This commit is contained in:
Masayoshi Okutsu 2010-10-20 14:41:39 +09:00
parent 8df28ab0e3
commit fbe778aead
5 changed files with 107 additions and 73 deletions

View File

@ -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<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 @@ 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<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;
}
/**

View File

@ -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.BigInteger;
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 class DecimalFormat extends NumberFormat {
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 @@ public class DecimalFormat extends NumberFormat {
/**
* 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);
}

View File

@ -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<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 @@ 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();
}
/**

View File

@ -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<Ca
* 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 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
// 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 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
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];

View File

@ -160,11 +160,6 @@ abstract public class TimeZone implements Serializable, Cloneable {
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;