This commit is contained in:
Looly 2023-04-03 19:12:09 +08:00
parent d6d7d8db75
commit 4d752ece91
3 changed files with 98 additions and 95 deletions

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.collection.queue;
import java.util.AbstractQueue;
import java.util.Collections;
import java.util.Iterator;
import java.util.Queue;
/**
* 始终为空的队列所有新增节点都丢弃
*
* @author Guava, looly
* @since 6.0.0
*/
public class DiscardingQueue extends AbstractQueue<Object> {
private static final DiscardingQueue INSTANCE = new DiscardingQueue();
/**
* 获取单例的空队列
* @return DiscardingQueue
* @param <E> 节点类型
*/
@SuppressWarnings("unchecked")
public static <E> Queue<E> getInstance() {
return (Queue<E>) INSTANCE;
}
@Override
public boolean add(final Object e) {
return true;
}
@Override
public boolean offer(final Object e) {
return true;
}
@Override
public Object poll() {
return null;
}
@Override
public Object peek() {
return null;
}
@Override
public int size() {
return 0;
}
@Override
public Iterator<Object> iterator() {
return Collections.emptyIterator();
}
}

View File

@ -15,9 +15,12 @@
*/ */
package org.dromara.hutool.core.map.concurrent; package org.dromara.hutool.core.map.concurrent;
import org.dromara.hutool.core.collection.queue.DiscardingQueue;
import org.dromara.hutool.core.collection.queue.Linked; import org.dromara.hutool.core.collection.queue.Linked;
import org.dromara.hutool.core.collection.queue.LinkedDeque; import org.dromara.hutool.core.collection.queue.LinkedDeque;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.map.SafeConcurrentHashMap; import org.dromara.hutool.core.map.SafeConcurrentHashMap;
import org.dromara.hutool.core.util.RuntimeUtil;
import java.io.InvalidObjectException; import java.io.InvalidObjectException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
@ -128,7 +131,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
/** /**
* The number of CPUs * The number of CPUs
*/ */
static final int NCPU = Runtime.getRuntime().availableProcessors(); static final int NCPU = RuntimeUtil.getProcessorCount();
/** /**
* The maximum weighted capacity of the map. * The maximum weighted capacity of the map.
@ -170,11 +173,6 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
*/ */
static final int WRITE_BUFFER_DRAIN_THRESHOLD = 16; static final int WRITE_BUFFER_DRAIN_THRESHOLD = 16;
/**
* A queue that discards all entries.
*/
static final Queue<?> DISCARDING_QUEUE = new DiscardingQueue();
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
static int ceilingNextPowerOfTwo(final int x) { static int ceilingNextPowerOfTwo(final int x) {
// From Hacker's Delight, Chapter 3, Harry S. Warren Jr. // From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
@ -243,35 +241,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
// The notification queue and listener // The notification queue and listener
listener = builder.listener; listener = builder.listener;
pendingNotifications = (listener == DiscardingListener.INSTANCE) pendingNotifications = (listener == DiscardingListener.INSTANCE)
? (Queue<Node<K, V>>) DISCARDING_QUEUE ? DiscardingQueue.getInstance() : new ConcurrentLinkedQueue<>();
: new ConcurrentLinkedQueue<>();
}
/**
* Ensures that the object is not null.
*/
static void checkNotNull(final Object o) {
if (o == null) {
throw new NullPointerException();
}
}
/**
* Ensures that the argument expression is true.
*/
static void checkArgument(final boolean expression) {
if (!expression) {
throw new IllegalArgumentException();
}
}
/**
* Ensures that the state expression is true.
*/
static void checkState(final boolean expression) {
if (!expression) {
throw new IllegalStateException();
}
} }
/* ---------------- Eviction Support -------------- */ /* ---------------- Eviction Support -------------- */
@ -293,7 +263,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
* @throws IllegalArgumentException if the capacity is negative * @throws IllegalArgumentException if the capacity is negative
*/ */
public void setCapacity(final long capacity) { public void setCapacity(final long capacity) {
checkArgument(capacity >= 0); Assert.isTrue(capacity >= 0);
evictionLock.lock(); evictionLock.lock();
try { try {
this.capacity.lazySet(Math.min(capacity, MAXIMUM_CAPACITY)); this.capacity.lazySet(Math.min(capacity, MAXIMUM_CAPACITY));
@ -679,7 +649,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
@Override @Override
public boolean containsValue(final Object value) { public boolean containsValue(final Object value) {
checkNotNull(value); Assert.notNull(value);
for (final Node<K, V> node : data.values()) { for (final Node<K, V> node : data.values()) {
if (node.getValue().equals(value)) { if (node.getValue().equals(value)) {
@ -736,8 +706,8 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
* @return the prior value in the data store or null if no mapping was found * @return the prior value in the data store or null if no mapping was found
*/ */
V put(final K key, final V value, final boolean onlyIfAbsent) { V put(final K key, final V value, final boolean onlyIfAbsent) {
checkNotNull(key); Assert.notNull(key);
checkNotNull(value); Assert.notNull(value);
final int weight = weigher.weightOf(key, value); final int weight = weigher.weightOf(key, value);
final WeightedValue<V> weightedValue = new WeightedValue<>(value, weight); final WeightedValue<V> weightedValue = new WeightedValue<>(value, weight);
@ -814,8 +784,8 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
@Override @Override
public V replace(final K key, final V value) { public V replace(final K key, final V value) {
checkNotNull(key); Assert.notNull(key);
checkNotNull(value); Assert.notNull(value);
final int weight = weigher.weightOf(key, value); final int weight = weigher.weightOf(key, value);
final WeightedValue<V> weightedValue = new WeightedValue<>(value, weight); final WeightedValue<V> weightedValue = new WeightedValue<>(value, weight);
@ -843,9 +813,9 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
@Override @Override
public boolean replace(final K key, final V oldValue, final V newValue) { public boolean replace(final K key, final V oldValue, final V newValue) {
checkNotNull(key); Assert.notNull(key);
checkNotNull(oldValue); Assert.notNull(oldValue);
checkNotNull(newValue); Assert.notNull(newValue);
final int weight = weigher.weightOf(key, newValue); final int weight = weigher.weightOf(key, newValue);
final WeightedValue<V> newWeightedValue = new WeightedValue<>(newValue, weight); final WeightedValue<V> newWeightedValue = new WeightedValue<>(newValue, weight);
@ -950,7 +920,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
} }
Set<K> orderedKeySet(final boolean ascending, final int limit) { Set<K> orderedKeySet(final boolean ascending, final int limit) {
checkArgument(limit >= 0); Assert.isTrue(limit >= 0);
evictionLock.lock(); evictionLock.lock();
try { try {
drainBuffers(); drainBuffers();
@ -1060,7 +1030,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
} }
Map<K, V> orderedMap(final boolean ascending, final int limit) { Map<K, V> orderedMap(final boolean ascending, final int limit) {
checkArgument(limit >= 0); Assert.isTrue(limit >= 0);
evictionLock.lock(); evictionLock.lock();
try { try {
drainBuffers(); drainBuffers();
@ -1277,7 +1247,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
@Override @Override
public void remove() { public void remove() {
checkState(current != null); Assert.state(current != null);
ConcurrentLinkedHashMap.this.remove(current); ConcurrentLinkedHashMap.this.remove(current);
current = null; current = null;
} }
@ -1329,7 +1299,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
@Override @Override
public void remove() { public void remove() {
checkState(current != null); Assert.state(current != null);
ConcurrentLinkedHashMap.this.remove(current.key); ConcurrentLinkedHashMap.this.remove(current.key);
current = null; current = null;
} }
@ -1403,7 +1373,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
@Override @Override
public void remove() { public void remove() {
checkState(current != null); Assert.state(current != null);
ConcurrentLinkedHashMap.this.remove(current.key); ConcurrentLinkedHashMap.this.remove(current.key);
current = null; current = null;
} }
@ -1438,14 +1408,14 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
final EntryWeigher<? super K, ? super V> weigher; final EntryWeigher<? super K, ? super V> weigher;
BoundedEntryWeigher(final EntryWeigher<? super K, ? super V> weigher) { BoundedEntryWeigher(final EntryWeigher<? super K, ? super V> weigher) {
checkNotNull(weigher); Assert.notNull(weigher);
this.weigher = weigher; this.weigher = weigher;
} }
@Override @Override
public int weightOf(final K key, final V value) { public int weightOf(final K key, final V value) {
final int weight = weigher.weightOf(key, value); final int weight = weigher.weightOf(key, value);
checkArgument(weight >= 1); Assert.isTrue(weight >= 1);
return weight; return weight;
} }
@ -1454,41 +1424,6 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
} }
} }
/**
* A queue that discards all additions and is always empty.
*/
static final class DiscardingQueue extends AbstractQueue<Object> {
@Override
public boolean add(final Object e) {
return true;
}
@Override
public boolean offer(final Object e) {
return true;
}
@Override
public Object poll() {
return null;
}
@Override
public Object peek() {
return null;
}
@Override
public int size() {
return 0;
}
@Override
public Iterator<Object> iterator() {
return Collections.emptyIterator();
}
}
/** /**
* A listener that ignores all notifications. * A listener that ignores all notifications.
*/ */
@ -1595,7 +1530,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
* @throws IllegalArgumentException if the initialCapacity is negative * @throws IllegalArgumentException if the initialCapacity is negative
*/ */
public Builder<K, V> initialCapacity(final int initialCapacity) { public Builder<K, V> initialCapacity(final int initialCapacity) {
checkArgument(initialCapacity >= 0); Assert.isTrue(initialCapacity >= 0);
this.initialCapacity = initialCapacity; this.initialCapacity = initialCapacity;
return this; return this;
} }
@ -1610,7 +1545,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
* negative * negative
*/ */
public Builder<K, V> maximumWeightedCapacity(final long capacity) { public Builder<K, V> maximumWeightedCapacity(final long capacity) {
checkArgument(capacity >= 0); Assert.isTrue(capacity >= 0);
this.capacity = capacity; this.capacity = capacity;
return this; return this;
} }
@ -1627,7 +1562,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
* equal to zero * equal to zero
*/ */
public Builder<K, V> concurrencyLevel(final int concurrencyLevel) { public Builder<K, V> concurrencyLevel(final int concurrencyLevel) {
checkArgument(concurrencyLevel > 0); Assert.isTrue(concurrencyLevel > 0);
this.concurrencyLevel = concurrencyLevel; this.concurrencyLevel = concurrencyLevel;
return this; return this;
} }
@ -1641,7 +1576,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
* @throws NullPointerException if the listener is null * @throws NullPointerException if the listener is null
*/ */
public Builder<K, V> listener(final BiConsumer<K, V> listener) { public Builder<K, V> listener(final BiConsumer<K, V> listener) {
checkNotNull(listener); Assert.notNull(listener);
this.listener = listener; this.listener = listener;
return this; return this;
} }
@ -1686,7 +1621,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
* not set * not set
*/ */
public ConcurrentLinkedHashMap<K, V> build() { public ConcurrentLinkedHashMap<K, V> build() {
checkState(capacity >= 0); Assert.state(capacity >= 0);
return new ConcurrentLinkedHashMap<>(this); return new ConcurrentLinkedHashMap<>(this);
} }
} }

View File

@ -18,19 +18,19 @@
* This package contains an implementation of a bounded * This package contains an implementation of a bounded
* {@link java.util.concurrent.ConcurrentMap} data structure. * {@link java.util.concurrent.ConcurrentMap} data structure.
* <p> * <p>
* {@link com.googlecode.concurrentlinkedhashmap.Weigher} is a simple interface * {@link org.dromara.hutool.core.map.concurrent.Weigher} is a simple interface
* for determining how many units of capacity an entry consumes. Depending on * for determining how many units of capacity an entry consumes. Depending on
* which concrete Weigher class is used, an entry may consume a different amount * which concrete Weigher class is used, an entry may consume a different amount
* of space within the cache. The * of space within the cache. The
* {@link com.googlecode.concurrentlinkedhashmap.Weighers} class provides * {@link org.dromara.hutool.core.map.concurrent.Weighers} class provides
* utility methods for obtaining the most common kinds of implementations. * utility methods for obtaining the most common kinds of implementations.
* <p> * <p>
* {@link com.googlecode.concurrentlinkedhashmap.EvictionListener} provides the * {@link org.dromara.hutool.core.map.concurrent.ConcurrentLinkedHashMap#listener} provides the
* ability to be notified when an entry is evicted from the map. An eviction * ability to be notified when an entry is evicted from the map. An eviction
* occurs when the entry was automatically removed due to the map exceeding a * occurs when the entry was automatically removed due to the map exceeding a
* capacity threshold. It is not called when an entry was explicitly removed. * capacity threshold. It is not called when an entry was explicitly removed.
* <p> * <p>
* The {@link com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap} * The {@link org.dromara.hutool.core.map.concurrent.ConcurrentLinkedHashMap}
* class supplies an efficient, scalable, thread-safe, bounded map. As with the * class supplies an efficient, scalable, thread-safe, bounded map. As with the
* <tt>Java Collections Framework</tt> the "Concurrent" prefix is used to * <tt>Java Collections Framework</tt> the "Concurrent" prefix is used to
* indicate that the map is not governed by a single exclusion lock. * indicate that the map is not governed by a single exclusion lock.