增加SafeConcurrentHashMap

This commit is contained in:
Looly
2022-09-15 09:50:54 +08:00
parent 920fe44e50
commit 06fec1045c
31 changed files with 168 additions and 105 deletions

View File

@@ -1,7 +1,6 @@
package cn.hutool.core.bean;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.WeakConcurrentMap;
/**
@@ -24,7 +23,7 @@ public enum BeanDescCache {
* @since 5.4.2
*/
public BeanDesc getBeanDesc(Class<?> beanClass, Func0<BeanDesc> supplier) {
return MapUtil.computeIfAbsent(bdCache, beanClass, (key)->supplier.callWithRuntimeException());
return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
}
/**

View File

@@ -1,12 +1,13 @@
package cn.hutool.core.collection;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
/**
* 通过{@link ConcurrentHashMap}实现的线程安全HashSet
* 通过{@link SafeConcurrentHashMap}实现的线程安全HashSet
*
* @author Looly
*
@@ -18,7 +19,7 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> implements java.io.Seri
/** 持有对象。如果值为此对象表示有数据,否则无数据 */
private static final Boolean PRESENT = true;
private final ConcurrentHashMap<E, Boolean> map;
private final SafeConcurrentHashMap<E, Boolean> map;
// ----------------------------------------------------------------------------------- Constructor start
/**
@@ -26,7 +27,7 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> implements java.io.Seri
* 触发因子为默认的0.75
*/
public ConcurrentHashSet() {
map = new ConcurrentHashMap<>();
map = new SafeConcurrentHashMap<>();
}
/**
@@ -36,7 +37,7 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> implements java.io.Seri
* @param initialCapacity 初始大小
*/
public ConcurrentHashSet(int initialCapacity) {
map = new ConcurrentHashMap<>(initialCapacity);
map = new SafeConcurrentHashMap<>(initialCapacity);
}
/**
@@ -46,7 +47,7 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> implements java.io.Seri
* @param loadFactor 加载因子。此参数决定数据增长时触发的百分比
*/
public ConcurrentHashSet(int initialCapacity, float loadFactor) {
map = new ConcurrentHashMap<>(initialCapacity, loadFactor);
map = new SafeConcurrentHashMap<>(initialCapacity, loadFactor);
}
/**
@@ -57,7 +58,7 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> implements java.io.Seri
* @param concurrencyLevel 线程并发度
*/
public ConcurrentHashSet(int initialCapacity, float loadFactor, int concurrencyLevel) {
map = new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
map = new SafeConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
}
/**
@@ -67,10 +68,10 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> implements java.io.Seri
public ConcurrentHashSet(Iterable<E> iter) {
if(iter instanceof Collection) {
final Collection<E> collection = (Collection<E>)iter;
map = new ConcurrentHashMap<>((int)(collection.size() / 0.75f));
map = new SafeConcurrentHashMap<>((int)(collection.size() / 0.75f));
this.addAll(collection);
}else {
map = new ConcurrentHashMap<>();
map = new SafeConcurrentHashMap<>();
for (E e : iter) {
this.add(e);
}

View File

@@ -1,7 +1,8 @@
package cn.hutool.core.convert;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 基本变量类型的枚举<br>
@@ -12,9 +13,9 @@ public enum BasicType {
BYTE, SHORT, INT, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, CHAR, CHARACTER, STRING;
/** 包装类型为Key原始类型为Value例如 Integer.class =》 int.class. */
public static final Map<Class<?>, Class<?>> WRAPPER_PRIMITIVE_MAP = new ConcurrentHashMap<>(8);
public static final Map<Class<?>, Class<?>> WRAPPER_PRIMITIVE_MAP = new SafeConcurrentHashMap<>(8);
/** 原始类型为Key包装类型为Value例如 int.class =》 Integer.class. */
public static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP = new ConcurrentHashMap<>(8);
public static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP = new SafeConcurrentHashMap<>(8);
static {
WRAPPER_PRIMITIVE_MAP.put(Boolean.class, boolean.class);

View File

@@ -36,6 +36,7 @@ import cn.hutool.core.convert.impl.UUIDConverter;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
@@ -74,7 +75,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
@@ -172,7 +172,7 @@ public class ConverterRegistry implements Serializable {
if (null == customConverterMap) {
synchronized (this) {
if (null == customConverterMap) {
customConverterMap = new ConcurrentHashMap<>();
customConverterMap = new SafeConcurrentHashMap<>();
}
}
}
@@ -384,7 +384,7 @@ public class ConverterRegistry implements Serializable {
* @return 转换器
*/
private ConverterRegistry defaultConverter() {
defaultConverterMap = new ConcurrentHashMap<>();
defaultConverterMap = new SafeConcurrentHashMap<>();
// 原始类型转换器
defaultConverterMap.put(int.class, new PrimitiveConverter(int.class));

View File

@@ -1,10 +1,10 @@
package cn.hutool.core.date;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.core.util.ObjectUtil;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 分组计时器<br>
@@ -26,7 +26,7 @@ public class GroupTimeInterval implements Serializable {
*/
public GroupTimeInterval(boolean isNano) {
this.isNano = isNano;
groupMap = new ConcurrentHashMap<>();
groupMap = new SafeConcurrentHashMap<>();
}
/**

View File

@@ -1,5 +1,7 @@
package cn.hutool.core.date.format;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.text.DateFormatSymbols;
@@ -18,7 +20,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -449,7 +450,7 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
private static ConcurrentMap<Locale, Strategy> getCache(final int field) {
synchronized (CACHES) {
if (CACHES[field] == null) {
CACHES[field] = new ConcurrentHashMap<>(3);
CACHES[field] = new SafeConcurrentHashMap<>(3);
}
return CACHES[field];
}

View File

@@ -1,6 +1,7 @@
package cn.hutool.core.date.format;
import cn.hutool.core.date.DateException;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.io.IOException;
import java.io.ObjectInputStream;
@@ -11,7 +12,6 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
@@ -1049,7 +1049,7 @@ public class FastDatePrinter extends AbstractDateBasic implements DatePrinter {
// -----------------------------------------------------------------------
private static final ConcurrentMap<TimeZoneDisplayKey, String> C_TIME_ZONE_DISPLAY_CACHE = new ConcurrentHashMap<>(7);
private static final ConcurrentMap<TimeZoneDisplayKey, String> C_TIME_ZONE_DISPLAY_CACHE = new SafeConcurrentHashMap<>(7);
/**
* <p>

View File

@@ -2,13 +2,13 @@ package cn.hutool.core.date.format;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.text.DateFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
@@ -24,9 +24,9 @@ abstract class FormatCache<F extends Format> {
*/
static final int NONE = -1;
private final ConcurrentMap<Tuple, F> cInstanceCache = new ConcurrentHashMap<>(7);
private final ConcurrentMap<Tuple, F> cInstanceCache = new SafeConcurrentHashMap<>(7);
private static final ConcurrentMap<Tuple, String> C_DATE_TIME_INSTANCE_CACHE = new ConcurrentHashMap<>(7);
private static final ConcurrentMap<Tuple, String> C_DATE_TIME_INSTANCE_CACHE = new SafeConcurrentHashMap<>(7);
/**
* 使用默认的pattern、timezone和locale获得缓存中的实例

View File

@@ -2,11 +2,11 @@ package cn.hutool.core.date.format;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
@@ -25,8 +25,8 @@ public class GlobalCustomFormat {
private static final Map<CharSequence, Function<CharSequence, Date>> parserMap;
static {
formatterMap = new ConcurrentHashMap<>();
parserMap = new ConcurrentHashMap<>();
formatterMap = new SafeConcurrentHashMap<>();
parserMap = new SafeConcurrentHashMap<>();
// Hutool预设的几种自定义格式
putFormatter(FORMAT_SECONDS, (date) -> String.valueOf(Math.floorDiv(date.getTime(), 1000)));

View File

@@ -4,14 +4,13 @@ import cn.hutool.core.collection.TransIter;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.lang.mutable.MutableObj;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.core.map.WeakConcurrentMap;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
@@ -37,7 +36,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
/**
* 写的时候每个key一把锁降低锁的粒度
*/
protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
protected final Map<K, Lock> keyLockMap = new SafeConcurrentHashMap<>();
/**
* 构造,默认使用{@link WeakHashMap}实现缓存自动清理
@@ -102,7 +101,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
}
if (null == v && null != supplier) {
//每个key单独获取一把锁降低锁的粒度提高并发能力see pr#1385@Github
final Lock keyLock = MapUtil.computeIfAbsent(this.keyLockMap, key, k -> new ReentrantLock());
final Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
keyLock.lock();
try {
// 双重检查,防止在竞争锁的过程中已经有其它线程写入

View File

@@ -1,14 +1,13 @@
package cn.hutool.core.lang;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
@@ -19,7 +18,7 @@ import java.util.stream.Collectors;
*/
public final class Singleton {
private static final ConcurrentHashMap<String, Object> POOL = new ConcurrentHashMap<>();
private static final SafeConcurrentHashMap<String, Object> POOL = new SafeConcurrentHashMap<>();
private Singleton() {
}
@@ -53,7 +52,7 @@ public final class Singleton {
*/
@SuppressWarnings("unchecked")
public static <T> T get(String key, Func0<T> supplier) {
return (T) MapUtil.computeIfAbsent(POOL, key, (k)-> supplier.callWithRuntimeException());
return (T) POOL.computeIfAbsent(key, (k)-> supplier.callWithRuntimeException());
}
/**

View File

@@ -1464,14 +1464,14 @@ public class MapUtil {
}
/**
* 方法来自Dubbo解决使用ConcurrentHashMap.computeIfAbsent导致的死循环问题。<br>
* issues#2349<br>
* 如果 key 对应的 value 不存在,则使用获取 mappingFunction 重新计算后的值,并保存为该 key 的 value否则返回 value。<br>
* 方法来自Dubbo解决使用ConcurrentHashMap.computeIfAbsent导致的死循环问题。issues#2349<br>
* A temporary workaround for Java 8 specific performance issue JDK-8161372 .<br>
* This class should be removed once we drop Java 8 support.
*
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-8161372">https://bugs.openjdk.java.net/browse/JDK-8161372</a>
*/
public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) {
public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<? super K, ? extends V> mappingFunction) {
V value = map.get(key);
if(null == value){
map.putIfAbsent(key, mappingFunction.apply(key));

View File

@@ -134,7 +134,7 @@ public class ReferenceConcurrentMap<K, V> implements ConcurrentMap<K, V>, Iterab
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
this.purgeStaleKeys();
return MapUtil.computeIfAbsent(this.raw, ofKey(key, this.lastQueue), kWeakKey -> mappingFunction.apply(key));
return this.raw.computeIfAbsent(ofKey(key, this.lastQueue), kWeakKey -> mappingFunction.apply(key));
}
@Override

View File

@@ -0,0 +1,74 @@
package cn.hutool.core.map;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* 安全的ConcurrentHashMap实现<br>
* 此类用于解决在JDK8中调用{@link ConcurrentHashMap#computeIfAbsent(Object, Function)}可能造成的死循环问题。<br>
* 方法来自Dubboissues#2349<br>
* <p>
* 相关bug见@see <a href="https://bugs.openjdk.java.net/browse/JDK-8161372">https://bugs.openjdk.java.net/browse/JDK-8161372</a>
*
* @param <K> 键类型
* @param <V> 值类型
*/
public class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
private static final long serialVersionUID = 1L;
// region == 构造 ==
/**
* 构造默认初始大小16
*/
public SafeConcurrentHashMap() {
super();
}
/**
* 构造
*
* @param initialCapacity 预估初始大小
*/
public SafeConcurrentHashMap(int initialCapacity) {
super(initialCapacity);
}
/**
* 构造
*
* @param m 初始键值对
*/
public SafeConcurrentHashMap(Map<? extends K, ? extends V> m) {
super(m);
}
/**
* 构造
*
* @param initialCapacity 初始容量
* @param loadFactor 增长系数
*/
public SafeConcurrentHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
/**
* 构造
*
* @param initialCapacity 初始容量
* @param loadFactor 增长系数
* @param concurrencyLevel 并发级别即Segment的个数
*/
public SafeConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
super(initialCapacity, loadFactor, concurrencyLevel);
}
// endregion == 构造 ==
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
return MapUtil.computeIfAbsent(this, key, mappingFunction);
}
}

View File

@@ -3,7 +3,6 @@ package cn.hutool.core.map;
import cn.hutool.core.util.ReferenceUtil;
import java.lang.ref.Reference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
@@ -21,7 +20,7 @@ public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
* 构造
*/
public WeakConcurrentMap() {
this(new ConcurrentHashMap<>());
this(new SafeConcurrentHashMap<>());
}
/**

View File

@@ -1,6 +1,7 @@
package cn.hutool.core.text;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
@@ -8,7 +9,6 @@ import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -62,9 +62,9 @@ public class AntPathMatcher {
private volatile Boolean cachePatterns;
private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<>(256);
private final Map<String, String[]> tokenizedPatternCache = new SafeConcurrentHashMap<>(256);
private final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<>(256);
private final Map<String, AntPathStringMatcher> stringMatcherCache = new SafeConcurrentHashMap<>(256);
/**

View File

@@ -5,7 +5,7 @@ import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.JarClassLoader;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.text.CharPool;
@@ -16,7 +16,6 @@ import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* {@link ClassLoader}工具类
@@ -50,7 +49,7 @@ public class ClassLoaderUtil {
/**
* 原始类型名和其class对应表例如int =》 int.class
*/
private static final Map<String, Class<?>> PRIMITIVE_TYPE_NAME_MAP = new ConcurrentHashMap<>(32);
private static final Map<String, Class<?>> PRIMITIVE_TYPE_NAME_MAP = new SafeConcurrentHashMap<>(32);
private static final Map<Pair<String, ClassLoader>, Class<?>> CLASS_CACHE = new WeakConcurrentMap<>();
static {
@@ -200,7 +199,7 @@ public class ClassLoaderUtil {
if (clazz == null) {
final String finalName = name;
final ClassLoader finalClassLoader = classLoader;
clazz = MapUtil.computeIfAbsent(CLASS_CACHE, Pair.of(name, classLoader), (key)-> doLoadClass(finalName, finalClassLoader, isInitialized));
clazz = CLASS_CACHE.computeIfAbsent(Pair.of(name, classLoader), (key)-> doLoadClass(finalName, finalClassLoader, isInitialized));
}
return clazz;
}

View File

@@ -1,9 +1,9 @@
package cn.hutool.core.util;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
/**
@@ -36,7 +36,7 @@ public class CreditCodeUtil {
private static final Map<Character, Integer> CODE_INDEX_MAP;
static {
CODE_INDEX_MAP = new ConcurrentHashMap<>();
CODE_INDEX_MAP = new SafeConcurrentHashMap<>(BASE_CODE_ARRAY.length);
for (int i = 0; i < BASE_CODE_ARRAY.length; i++) {
CODE_INDEX_MAP.put(BASE_CODE_ARRAY[i], i);
}