diff --git a/src/java.base/share/classes/java/util/ResourceBundle.java b/src/java.base/share/classes/java/util/ResourceBundle.java index fb88a38903a62..b3807ac770ae9 100644 --- a/src/java.base/share/classes/java/util/ResourceBundle.java +++ b/src/java.base/share/classes/java/util/ResourceBundle.java @@ -54,6 +54,7 @@ import java.net.URLConnection; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Supplier; import java.util.jar.JarEntry; import java.util.spi.ResourceBundleControlProvider; import java.util.spi.ResourceBundleProvider; @@ -487,7 +488,20 @@ public String getBaseBundleName() { /** * A Set of the keys contained only in this ResourceBundle. */ - private volatile Set keySet; + private final Supplier> keySet = StableValue.supplier( + new Supplier<>() { public Set get() { return keySet0(); }}); + + private Set keySet0() { + final Set keys = new HashSet<>(); + final Enumeration enumKeys = getKeys(); + while (enumKeys.hasMoreElements()) { + final String key = enumKeys.nextElement(); + if (handleGetObject(key) != null) { + keys.add(key); + } + } + return keys; + } /** * Sole constructor. (For invocation by subclass constructors, typically @@ -2298,22 +2312,7 @@ public Set keySet() { * @since 1.6 */ protected Set handleKeySet() { - if (keySet == null) { - synchronized (this) { - if (keySet == null) { - Set keys = new HashSet<>(); - Enumeration enumKeys = getKeys(); - while (enumKeys.hasMoreElements()) { - String key = enumKeys.nextElement(); - if (handleGetObject(key) != null) { - keys.add(key); - } - } - keySet = keys; - } - } - } - return keySet; + return keySet.get(); } diff --git a/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java b/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java index 3a55e7ccb8c58..d7c575962f627 100644 --- a/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java +++ b/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -30,6 +30,7 @@ import java.util.Enumeration; import java.util.ResourceBundle; import java.util.Set; +import java.util.function.Supplier; /** * BreakIteratorResourceBundle is an abstract class for loading BreakIterator @@ -49,7 +50,15 @@ public abstract class BreakIteratorResourceBundle extends ResourceBundle { // those keys must be added to NON_DATA_KEYS. private static final Set NON_DATA_KEYS = Set.of("BreakIteratorClasses"); - private volatile Set keys; + private final Supplier> keys = StableValue.supplier( + new Supplier<>() { public Set get() { return keys0(); }}); + + private Set keys0() { + final ResourceBundle info = getBreakIteratorInfo(); + final Set k = info.keySet(); + k.removeAll(NON_DATA_KEYS); + return k; + } /** * Returns an instance of the corresponding {@code BreakIteratorInfo} (basename). @@ -84,16 +93,6 @@ public Enumeration getKeys() { @Override protected Set handleKeySet() { - if (keys == null) { - ResourceBundle info = getBreakIteratorInfo(); - Set k = info.keySet(); - k.removeAll(NON_DATA_KEYS); - synchronized (this) { - if (keys == null) { - keys = k; - } - } - } - return keys; + return keys.get(); } } diff --git a/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java b/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java index d375930dde066..0c16f50801875 100644 --- a/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java +++ b/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -44,8 +44,11 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.ResourceBundle; import java.util.Set; +import java.util.function.Supplier; + import sun.util.ResourceBundleEnumeration; /** @@ -69,12 +72,9 @@ protected OpenListResourceBundle() { // Implements java.util.ResourceBundle.handleGetObject; inherits javadoc specification. @Override protected Object handleGetObject(String key) { - if (key == null) { - throw new NullPointerException(); - } - - loadLookupTablesIfNecessary(); - return lookup.get(key); // this class ignores locales + Objects.requireNonNull(key); + return lookup.get() + .get(key); // this class ignores locales } /** @@ -93,26 +93,13 @@ public Enumeration getKeys() { */ @Override protected Set handleKeySet() { - loadLookupTablesIfNecessary(); - return lookup.keySet(); + return lookup.get() + .keySet(); } @Override public Set keySet() { - if (keyset != null) { - return keyset; - } - Set ks = createSet(); - ks.addAll(handleKeySet()); - if (parent != null) { - ks.addAll(parent.keySet()); - } - synchronized (this) { - if (keyset == null) { - keyset = ks; - } - } - return keyset; + return keyset.get(); } /** @@ -120,38 +107,6 @@ public Set keySet() { */ protected abstract Object[][] getContents(); - /** - * Load lookup tables if they haven't been loaded already. - */ - void loadLookupTablesIfNecessary() { - if (lookup == null) { - loadLookup(); - } - } - - /** - * We lazily load the lookup hashtable. This function does the - * loading. - */ - private void loadLookup() { - Object[][] contents = getContents(); - Map temp = createMap(contents.length); - for (int i = 0; i < contents.length; ++i) { - // key must be non-null String, value must be non-null - String key = (String) contents[i][0]; - Object value = contents[i][1]; - if (key == null || value == null) { - throw new NullPointerException(); - } - temp.put(key, value); - } - synchronized (this) { - if (lookup == null) { - lookup = temp; - } - } - } - /** * Lets subclasses provide specialized Map implementations. * Default uses HashMap. @@ -164,6 +119,31 @@ protected Set createSet() { return new HashSet<>(); } - private volatile Map lookup; - private volatile Set keyset; + private final Supplier> lookup = StableValue.supplier( + new Supplier<>() { public Map get() { return lookup0(); }}); + + private Map lookup0() { + final Object[][] contents = getContents(); + final Map temp = createMap(contents.length); + for (Object[] content : contents) { + // key must be non-null String, value must be non-null + final String key = Objects.requireNonNull((String) content[0]); + final Object value = Objects.requireNonNull(content[1]); + temp.put(key, value); + } + return temp; + } + + private final Supplier> keyset = StableValue.supplier( + new Supplier<>() { public Set get() { return keyset0(); }}); + + private Set keyset0() { + final Set ks = createSet(); + ks.addAll(handleKeySet()); + if (parent != null) { + ks.addAll(parent.keySet()); + } + return ks; + } + }