diff --git a/spring-core/src/main/java/org/springframework/util/ConcurrentLruCache.java b/spring-core/src/main/java/org/springframework/util/ConcurrentLruCache.java index 2e5f19e17613..8e93e2449851 100644 --- a/spring-core/src/main/java/org/springframework/util/ConcurrentLruCache.java +++ b/spring-core/src/main/java/org/springframework/util/ConcurrentLruCache.java @@ -21,8 +21,9 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; @@ -31,7 +32,7 @@ /** * Simple LRU (Least Recently Used) cache, bounded by a specified cache capacity. - *

This is a simplified, opinionated implementation of a LRU cache for internal + *

This is a simplified, opinionated implementation of an LRU cache for internal * use in Spring Framework. It is inspired from * ConcurrentLinkedHashMap. *

Read and write operations are internally recorded in dedicated buffers, @@ -358,7 +359,8 @@ private static final class ReadOperations { private static int detectNumberOfBuffers() { int availableProcessors = Runtime.getRuntime().availableProcessors(); - return 1 << (Integer.SIZE - Integer.numberOfLeadingZeros(availableProcessors - 1)); + int nextPowerOfTwo = 1 << (Integer.SIZE - Integer.numberOfLeadingZeros(availableProcessors - 1)); + return Math.min(4, nextPowerOfTwo); } private static final int BUFFERS_MASK = BUFFER_COUNT - 1; @@ -374,7 +376,7 @@ private static int detectNumberOfBuffers() { /* * Number of operations recorded, for each buffer */ - private final AtomicLong[] recordedCount = new AtomicLong[BUFFER_COUNT]; + private final AtomicLongArray recordedCount = new AtomicLongArray(BUFFER_COUNT); /* * Number of operations read, for each buffer @@ -384,10 +386,10 @@ private static int detectNumberOfBuffers() { /* * Number of operations processed, for each buffer */ - private final AtomicLong[] processedCount = new AtomicLong[BUFFER_COUNT]; + private final AtomicLongArray processedCount = new AtomicLongArray(BUFFER_COUNT); @SuppressWarnings("rawtypes") - private final AtomicReference>[][] buffers = new AtomicReference[BUFFER_COUNT][BUFFER_SIZE]; + private final AtomicReferenceArray>[] buffers = new AtomicReferenceArray[BUFFER_COUNT]; private final EvictionQueue evictionQueue; @@ -395,12 +397,7 @@ private static int detectNumberOfBuffers() { ReadOperations(EvictionQueue evictionQueue) { this.evictionQueue = evictionQueue; for (int i = 0; i < BUFFER_COUNT; i++) { - this.recordedCount[i] = new AtomicLong(); - this.processedCount[i] = new AtomicLong(); - this.buffers[i] = new AtomicReference[BUFFER_SIZE]; - for (int j = 0; j < BUFFER_SIZE; j++) { - this.buffers[i][j] = new AtomicReference<>(); - } + this.buffers[i] = new AtomicReferenceArray<>(BUFFER_SIZE); } } @@ -410,12 +407,11 @@ private static int getBufferIndex() { boolean recordRead(Node node) { int bufferIndex = getBufferIndex(); - final AtomicLong counter = this.recordedCount[bufferIndex]; - final long writeCount = counter.get(); - counter.lazySet(writeCount + 1); + final long writeCount = this.recordedCount.get(bufferIndex); + this.recordedCount.lazySet(bufferIndex, writeCount + 1); final int index = (int) (writeCount & BUFFER_INDEX_MASK); - this.buffers[bufferIndex][index].lazySet(node); - final long pending = (writeCount - this.processedCount[bufferIndex].get()); + this.buffers[bufferIndex].lazySet(index, node); + final long pending = (writeCount - this.processedCount.get(bufferIndex)); return (pending < MAX_PENDING_OPERATIONS); } @@ -428,27 +424,28 @@ void drain() { } void clear() { - for (AtomicReference>[] buffer : this.buffers) { - for (AtomicReference> slot : buffer) { - slot.lazySet(null); + for (int i = 0; i < BUFFER_COUNT; i++) { + AtomicReferenceArray> buffer = this.buffers[i]; + for (int j = 0; j < BUFFER_SIZE; j++) { + buffer.lazySet(j, null); } } } private void drainReadBuffer(int bufferIndex) { - final long writeCount = this.recordedCount[bufferIndex].get(); + final long writeCount = this.recordedCount.get(bufferIndex); for (int i = 0; i < MAX_DRAIN_COUNT; i++) { final int index = (int) (this.readCount[bufferIndex] & BUFFER_INDEX_MASK); - final AtomicReference> slot = this.buffers[bufferIndex][index]; - final Node node = slot.get(); + final AtomicReferenceArray> buffer = this.buffers[bufferIndex]; + final Node node = buffer.get(index); if (node == null) { break; } - slot.lazySet(null); + buffer.lazySet(index, null); this.evictionQueue.moveToBack(node); this.readCount[bufferIndex]++; } - this.processedCount[bufferIndex].lazySet(writeCount); + this.processedCount.lazySet(bufferIndex, writeCount); } }