diff --git a/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java b/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java index d242c789aa1..6a18ae7d907 100644 --- a/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java +++ b/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java @@ -35,6 +35,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import org.springframework.lang.Nullable; +import org.springframework.util.ConcurrentReferenceHashMap.Reference; +import org.springframework.util.ConcurrentReferenceHashMap.Restructure; /** * A {@link ConcurrentHashMap} that uses {@link ReferenceType#SOFT soft} or @@ -232,27 +234,24 @@ public class ConcurrentReferenceHashMap extends AbstractMap implemen @Override @Nullable public V get(@Nullable Object key) { - Entry entry = getEntryIfAvailable(key); + Reference ref = getReference(key, Restructure.WHEN_NECESSARY); + Entry entry = (ref != null ? ref.get() : null); return (entry != null ? entry.getValue() : null); } @Override @Nullable public V getOrDefault(@Nullable Object key, @Nullable V defaultValue) { - Entry entry = getEntryIfAvailable(key); + Reference ref = getReference(key, Restructure.WHEN_NECESSARY); + Entry entry = (ref != null ? ref.get() : null); return (entry != null ? entry.getValue() : defaultValue); } @Override public boolean containsKey(@Nullable Object key) { - Entry entry = getEntryIfAvailable(key); - return (entry != null && ObjectUtils.nullSafeEquals(entry.getKey(), key)); - } - - @Nullable - private Entry getEntryIfAvailable(@Nullable Object key) { Reference ref = getReference(key, Restructure.WHEN_NECESSARY); - return (ref != null ? ref.get() : null); + Entry entry = (ref != null ? ref.get() : null); + return (entry != null && ObjectUtils.nullSafeEquals(entry.getKey(), key)); } /** @@ -573,65 +572,70 @@ public class ConcurrentReferenceHashMap extends AbstractMap implemen */ protected final void restructureIfNecessary(boolean allowResize) { int currCount = this.count.get(); - boolean needsResize = (currCount > 0 && currCount >= this.resizeThreshold); + boolean needsResize = allowResize && (currCount > 0 && currCount >= this.resizeThreshold); Reference ref = this.referenceManager.pollForPurge(); - if (ref != null || (needsResize && allowResize)) { - lock(); - try { - int countAfterRestructure = this.count.get(); - Set> toPurge = Collections.emptySet(); - if (ref != null) { - toPurge = new HashSet<>(); - while (ref != null) { - toPurge.add(ref); - ref = this.referenceManager.pollForPurge(); - } - } - countAfterRestructure -= toPurge.size(); - - // Recalculate taking into account count inside lock and items that - // will be purged - needsResize = (countAfterRestructure > 0 && countAfterRestructure >= this.resizeThreshold); - boolean resizing = false; - int restructureSize = this.references.length; - if (allowResize && needsResize && restructureSize < MAXIMUM_SEGMENT_SIZE) { - restructureSize <<= 1; - resizing = true; + if (ref != null || (needsResize)) { + restructure(allowResize, ref); + } + } + + private void restructure(boolean allowResize, Reference ref) { + boolean needsResize; + lock(); + try { + int countAfterRestructure = this.count.get(); + Set> toPurge = Collections.emptySet(); + if (ref != null) { + toPurge = new HashSet<>(); + while (ref != null) { + toPurge.add(ref); + ref = this.referenceManager.pollForPurge(); } + } + countAfterRestructure -= toPurge.size(); + + // Recalculate taking into account count inside lock and items that + // will be purged + needsResize = (countAfterRestructure > 0 && countAfterRestructure >= this.resizeThreshold); + boolean resizing = false; + int restructureSize = this.references.length; + if (allowResize && needsResize && restructureSize < MAXIMUM_SEGMENT_SIZE) { + restructureSize <<= 1; + resizing = true; + } - // Either create a new table or reuse the existing one - Reference[] restructured = - (resizing ? createReferenceArray(restructureSize) : this.references); + // Either create a new table or reuse the existing one + Reference[] restructured = + (resizing ? createReferenceArray(restructureSize) : this.references); - // Restructure - for (int i = 0; i < this.references.length; i++) { - ref = this.references[i]; - if (!resizing) { - restructured[i] = null; - } - while (ref != null) { - if (!toPurge.contains(ref)) { - Entry entry = ref.get(); - if (entry != null) { - int index = getIndex(ref.getHash(), restructured); - restructured[index] = this.referenceManager.createReference( - entry, ref.getHash(), restructured[index]); - } + // Restructure + for (int i = 0; i < this.references.length; i++) { + ref = this.references[i]; + if (!resizing) { + restructured[i] = null; + } + while (ref != null) { + if (!toPurge.contains(ref)) { + Entry entry = ref.get(); + if (entry != null) { + int index = getIndex(ref.getHash(), restructured); + restructured[index] = this.referenceManager.createReference( + entry, ref.getHash(), restructured[index]); } - ref = ref.getNext(); } + ref = ref.getNext(); } - - // Replace volatile members - if (resizing) { - this.references = restructured; - this.resizeThreshold = (int) (this.references.length * getLoadFactor()); - } - this.count.set(Math.max(countAfterRestructure, 0)); } - finally { - unlock(); + + // Replace volatile members + if (resizing) { + this.references = restructured; + this.resizeThreshold = (int) (this.references.length * getLoadFactor()); } + this.count.set(Math.max(countAfterRestructure, 0)); + } + finally { + unlock(); } }