diff --git a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java index 573187ba3d0cd..1aa40ea16facd 100644 --- a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, 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 @@ -39,6 +39,7 @@ import java.util.spi.CalendarDataProvider; import java.util.spi.CalendarNameProvider; import java.util.spi.TimeZoneNameProvider; +import jdk.internal.vm.annotation.Stable; import sun.util.locale.provider.JRELocaleProviderAdapter; import sun.util.locale.provider.LocaleDataMetaInfo; import sun.util.locale.provider.LocaleProviderAdapter; @@ -60,6 +61,7 @@ public class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter { // cache to hold locale to locale mapping for language aliases. private static final Map langAliasesCache; // cache the available locales + @Stable private static volatile Locale[] AVAILABLE_LOCALES; static { diff --git a/src/java.base/share/classes/sun/util/locale/provider/FallbackLocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/locale/provider/FallbackLocaleProviderAdapter.java index 0637be76781eb..0d5bdcddec5d0 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/FallbackLocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/locale/provider/FallbackLocaleProviderAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, 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 @@ -32,6 +32,8 @@ import java.util.Set; import java.util.stream.Stream; +import jdk.internal.vm.annotation.Stable; + /* * FallbackProviderAdapter implementation. Fallback provider serves the * following purposes: @@ -46,6 +48,7 @@ public class FallbackLocaleProviderAdapter extends JRELocaleProviderAdapter { private static final Locale[] AVAILABLE_LOCS = {Locale.US, Locale.ENGLISH, Locale.ROOT}; private static final Set AVAILABLE_LANGTAGS = Set.of("en-US", "en", "und"); + @Stable private volatile BreakIteratorProvider breakIteratorProvider; /** diff --git a/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java index 14d36f0e266db..d78e352b04479 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, 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,6 +46,7 @@ import java.util.spi.LocaleNameProvider; import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; +import jdk.internal.vm.annotation.Stable; import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.resources.LocaleData; import sun.util.spi.CalendarProvider; @@ -65,6 +66,7 @@ public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements R = new ConcurrentHashMap<>(); // LocaleData specific to this LocaleProviderAdapter. + @Stable private volatile LocaleData localeData; /** @@ -81,52 +83,49 @@ public LocaleProviderAdapter.Type getAdapterType() { @Override @SuppressWarnings("unchecked") public

P getLocaleServiceProvider(Class

c) { - switch (c.getSimpleName()) { - case "BreakIteratorProvider": - return (P) getBreakIteratorProvider(); - case "CollatorProvider": - return (P) getCollatorProvider(); - case "DateFormatProvider": - return (P) getDateFormatProvider(); - case "DateFormatSymbolsProvider": - return (P) getDateFormatSymbolsProvider(); - case "DecimalFormatSymbolsProvider": - return (P) getDecimalFormatSymbolsProvider(); - case "NumberFormatProvider": - return (P) getNumberFormatProvider(); - case "CurrencyNameProvider": - return (P) getCurrencyNameProvider(); - case "LocaleNameProvider": - return (P) getLocaleNameProvider(); - case "TimeZoneNameProvider": - return (P) getTimeZoneNameProvider(); - case "CalendarDataProvider": - return (P) getCalendarDataProvider(); - case "CalendarNameProvider": - return (P) getCalendarNameProvider(); - case "CalendarProvider": - return (P) getCalendarProvider(); - case "JavaTimeDateTimePatternProvider": - return (P) getJavaTimeDateTimePatternProvider(); - default: - throw new InternalError("should not come down here"); - } + if (c == BreakIteratorProvider.class) return (P) getBreakIteratorProvider(); + if (c == CollatorProvider.class) return (P) getCollatorProvider(); + if (c == DateFormatProvider.class) return (P) getDateFormatProvider(); + if (c == DateFormatSymbolsProvider.class) return (P) getDateFormatSymbolsProvider(); + if (c == DecimalFormatSymbolsProvider.class) return (P) getDecimalFormatSymbolsProvider(); + if (c == NumberFormatProvider.class) return (P) getNumberFormatProvider(); + if (c == CurrencyNameProvider.class) return (P) getCurrencyNameProvider(); + if (c == LocaleNameProvider.class) return (P) getLocaleNameProvider(); + if (c == TimeZoneNameProvider.class) return (P) getTimeZoneNameProvider(); + if (c == CalendarDataProvider.class) return (P) getCalendarDataProvider(); + if (c == CalendarNameProvider.class) return (P) getCalendarNameProvider(); + if (c == CalendarProvider.class) return (P) getCalendarProvider(); + if (c == JavaTimeDateTimePatternProvider.class) return (P) getJavaTimeDateTimePatternProvider(); + throw new InternalError("should not come down here"); } + @Stable private volatile BreakIteratorProvider breakIteratorProvider; + @Stable private volatile CollatorProvider collatorProvider; + @Stable private volatile DateFormatProvider dateFormatProvider; + @Stable private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider; + @Stable private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider; + @Stable private volatile NumberFormatProvider numberFormatProvider; + @Stable private volatile CurrencyNameProvider currencyNameProvider; + @Stable private volatile LocaleNameProvider localeNameProvider; + @Stable protected volatile TimeZoneNameProvider timeZoneNameProvider; + @Stable protected volatile CalendarDataProvider calendarDataProvider; + @Stable protected volatile CalendarNameProvider calendarNameProvider; + @Stable private volatile CalendarProvider calendarProvider; + @Stable private volatile JavaTimeDateTimePatternProvider javaTimeDateTimePatternProvider; /* diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java index 5e7a4c719d160..820e80342df7d 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, 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 @@ -48,6 +48,7 @@ import java.util.spi.LocaleNameProvider; import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; +import jdk.internal.vm.annotation.Stable; import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.spi.CalendarProvider; @@ -64,30 +65,22 @@ public abstract class LocaleProviderAdapter { * Adapter type. */ public enum Type { - JRE("sun.util.locale.provider.JRELocaleProviderAdapter", "sun.util.resources", "sun.text.resources"), - CLDR("sun.util.cldr.CLDRLocaleProviderAdapter", "sun.util.resources.cldr", "sun.text.resources.cldr"), - SPI("sun.util.locale.provider.SPILocaleProviderAdapter"), - HOST("sun.util.locale.provider.HostLocaleProviderAdapter"), - FALLBACK("sun.util.locale.provider.FallbackLocaleProviderAdapter", "sun.util.resources", "sun.text.resources"); + JRE("sun.util.resources", "sun.text.resources"), + CLDR("sun.util.resources.cldr", "sun.text.resources.cldr"), + SPI(null, null), + HOST(null, null), + FALLBACK("sun.util.resources", "sun.text.resources"); - private final String CLASSNAME; private final String UTIL_RESOURCES_PACKAGE; private final String TEXT_RESOURCES_PACKAGE; + @Stable + private volatile LocaleProviderAdapter adapter; - Type(String className) { - this(className, null, null); - } - - Type(String className, String util, String text) { - CLASSNAME = className; + Type(String util, String text) { UTIL_RESOURCES_PACKAGE = util; TEXT_RESOURCES_PACKAGE = text; } - public String getAdapterClassName() { - return CLASSNAME; - } - public String getUtilResourcesPackage() { return UTIL_RESOURCES_PACKAGE; } @@ -95,6 +88,29 @@ public String getUtilResourcesPackage() { public String getTextResourcesPackage() { return TEXT_RESOURCES_PACKAGE; } + + public LocaleProviderAdapter getAdapter() { + if (adapter != null) { + return adapter; + } + return getAdapter0(); + } + + private synchronized LocaleProviderAdapter getAdapter0() { + if (adapter == null) { + // lazily load adapters here + adapter = switch (this) { + case JRE -> new JRELocaleProviderAdapter(); + case CLDR -> new sun.util.cldr.CLDRLocaleProviderAdapter(); + case SPI -> new SPILocaleProviderAdapter(); + case HOST -> new HostLocaleProviderAdapter(); + case FALLBACK -> new FallbackLocaleProviderAdapter(); + default -> throw new ServiceConfigurationError("Locale provider adapter \"" + + this + "\"cannot be instantiated."); + }; + } + return adapter; + } } /** @@ -102,16 +118,23 @@ public String getTextResourcesPackage() { */ private static final List adapterPreference; - /** - * LocaleProviderAdapter instances - */ - private static final Map adapterInstances = new ConcurrentHashMap<>(); - /** * Adapter lookup cache. */ - private static final ConcurrentMap, ConcurrentMap> - adapterCache = new ConcurrentHashMap<>(); + private static final ConcurrentMap + breakIteratorProviderAdapterMap = new ConcurrentHashMap<>(), + collatorProviderAdapterMap = new ConcurrentHashMap<>(), + dateFormatProviderAdapterMap = new ConcurrentHashMap<>(), + dateFormatSymbolsProviderAdapterMap = new ConcurrentHashMap<>(), + decimalFormatSymbolsProviderAdapterMap = new ConcurrentHashMap<>(), + numberFormatProviderAdapterMap = new ConcurrentHashMap<>(), + currencyNameProviderAdapterMap = new ConcurrentHashMap<>(), + localeNameProviderAdapterMap = new ConcurrentHashMap<>(), + timeZoneNameProviderAdapterMap = new ConcurrentHashMap<>(), + calendarDataProviderAdapterMap = new ConcurrentHashMap<>(), + calendarNameProviderAdapterMap = new ConcurrentHashMap<>(), + calendarProviderAdapterMap = new ConcurrentHashMap<>(), + javaTimeDateTimePatternProviderAdapterMap = new ConcurrentHashMap<>(); static { String order = System.getProperty("java.locale.providers"); @@ -167,49 +190,17 @@ public String getTextResourcesPackage() { * Returns the singleton instance for each adapter type */ public static LocaleProviderAdapter forType(Type type) { - switch (type) { - case JRE: - case CLDR: - case SPI: - case HOST: - case FALLBACK: - LocaleProviderAdapter adapter = adapterInstances.get(type); - if (adapter == null) { - try { - // lazily load adapters here - adapter = (LocaleProviderAdapter)Class.forName(type.getAdapterClassName()) - .getDeclaredConstructor().newInstance(); - LocaleProviderAdapter cached = adapterInstances.putIfAbsent(type, adapter); - if (cached != null) { - adapter = cached; - } - } catch (NoSuchMethodException | - InvocationTargetException | - ClassNotFoundException | - IllegalAccessException | - InstantiationException | - UnsupportedOperationException e) { - throw new ServiceConfigurationError("Locale provider adapter \"" + - type + "\"cannot be instantiated.", e); - } - } - return adapter; - default: - throw new InternalError("unknown locale data adapter type"); - } + return type.getAdapter(); } public static LocaleProviderAdapter forJRE() { - return forType(Type.JRE); + return Type.JRE.getAdapter(); } public static LocaleProviderAdapter getResourceBundleBased() { for (Type type : getAdapterPreference()) { if (type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK) { - LocaleProviderAdapter adapter = forType(type); - if (adapter != null) { - return adapter; - } + return type.getAdapter(); } } // Shouldn't happen. @@ -223,6 +214,24 @@ public static List getAdapterPreference() { return adapterPreference; } + @SuppressWarnings("unchecked") + private static ConcurrentMap getAdapterMap(Class providerClass) { + if (providerClass == BreakIteratorProvider.class) return breakIteratorProviderAdapterMap; + if (providerClass == CollatorProvider.class) return collatorProviderAdapterMap; + if (providerClass == DateFormatProvider.class) return dateFormatProviderAdapterMap; + if (providerClass == DateFormatSymbolsProvider.class) return dateFormatSymbolsProviderAdapterMap; + if (providerClass == DecimalFormatSymbolsProvider.class) return decimalFormatSymbolsProviderAdapterMap; + if (providerClass == NumberFormatProvider.class) return numberFormatProviderAdapterMap; + if (providerClass == CurrencyNameProvider.class) return currencyNameProviderAdapterMap; + if (providerClass == LocaleNameProvider.class) return localeNameProviderAdapterMap; + if (providerClass == TimeZoneNameProvider.class) return timeZoneNameProviderAdapterMap; + if (providerClass == CalendarDataProvider.class) return calendarDataProviderAdapterMap; + if (providerClass == CalendarNameProvider.class) return calendarNameProviderAdapterMap; + if (providerClass == CalendarProvider.class) return calendarProviderAdapterMap; + if (providerClass == JavaTimeDateTimePatternProvider.class) return javaTimeDateTimePatternProviderAdapterMap; + throw new InternalError("should not come down here"); + } + /** * Returns a LocaleProviderAdapter for the given locale service provider that * best matches the given locale. This method returns the LocaleProviderAdapter @@ -237,23 +246,23 @@ public static LocaleProviderAdapter getAdapter(Class adapterMap = adapterCache.get(providerClass); - if (adapterMap != null) { - if ((adapter = adapterMap.get(locale)) != null) { - return adapter; - } - } else { - adapterMap = new ConcurrentHashMap<>(); - adapterCache.putIfAbsent(providerClass, adapterMap); + ConcurrentMap adapterMap = getAdapterMap(providerClass); + if ((adapter = adapterMap.get(locale)) != null) { + return adapter; } + return getAdapter0(providerClass, locale, adapterMap); + } + + private static LocaleProviderAdapter getAdapter0(Class providerClass, + Locale locale, + ConcurrentMap adapterMap) { // Fast look-up for the given locale - adapter = findAdapter(providerClass, locale); + LocaleProviderAdapter adapter = findAdapter(providerClass, locale); if (adapter != null) { adapterMap.putIfAbsent(locale, adapter); return adapter; } - // Try finding an adapter in the normal candidate locales path of the given locale. List lookupLocales = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT) .getCandidateLocales("", locale); @@ -270,20 +279,18 @@ public static LocaleProviderAdapter getAdapter(Class providerClass, Locale locale) { for (Type type : getAdapterPreference()) { - LocaleProviderAdapter adapter = forType(type); - if (adapter != null) { - LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass); - if (provider != null) { - if (provider.isSupportedLocale(locale)) { - return adapter; - } + LocaleProviderAdapter adapter = type.getAdapter(); + LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass); + if (provider != null) { + if (provider.isSupportedLocale(locale)) { + return adapter; } } } diff --git a/test/micro/org/openjdk/bench/java/text/NumberFormatBench.java b/test/micro/org/openjdk/bench/java/text/NumberFormatBench.java new file mode 100644 index 0000000000000..99f87054efada --- /dev/null +++ b/test/micro/org/openjdk/bench/java/text/NumberFormatBench.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.text; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.*; + +import java.text.NumberFormat; +import java.util.Locale; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Thread) +public class NumberFormatBench { + + @Benchmark + public void getInstance() { + NumberFormat.getInstance(Locale.ENGLISH); + } +}