diff --git a/CHANGELOG.md b/CHANGELOG.md index c28f6fc09..294bd185f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * 【core 】 新增AnsiColors(改自Spring Boot)、AnsiColorWrapper,优化QrCodeUtil(pr#778@Gitee) * 【core 】 TemplateUtil的实现类增加getRawEngine方法(issues#2530@Github) * 【core 】 ImgUtil中颜色相关方法剥离到ColorUtil中 +* 【core 】 增加SafeConcurrentHashMap ### 🐞Bug修复 * 【core 】 修复ObjectUtil.defaultIfXXX中NPE问题(pr#2603@Github) diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java index a52ac4713..60de71688 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java @@ -5,11 +5,11 @@ import cn.hutool.cache.CacheListener; 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.SafeConcurrentHashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -35,7 +35,7 @@ public abstract class AbstractCache implements Cache { /** * 写的时候每个key一把锁,降低锁的粒度 */ - protected final Map keyLockMap = new ConcurrentHashMap<>(); + protected final SafeConcurrentHashMap keyLockMap = new SafeConcurrentHashMap<>(); /** * 返回缓存容量,{@code 0}表示无大小限制 diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java index 3cd993dcf..fa29e1d94 100755 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java @@ -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 supplier) { - return MapUtil.computeIfAbsent(bdCache, beanClass, (key)->supplier.callWithRuntimeException()); + return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException()); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/ConcurrentHashSet.java b/hutool-core/src/main/java/cn/hutool/core/collection/ConcurrentHashSet.java index 7e1229e08..bac6e1b49 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/ConcurrentHashSet.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/ConcurrentHashSet.java @@ -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 extends AbstractSet implements java.io.Seri /** 持有对象。如果值为此对象表示有数据,否则无数据 */ private static final Boolean PRESENT = true; - private final ConcurrentHashMap map; + private final SafeConcurrentHashMap map; // ----------------------------------------------------------------------------------- Constructor start /** @@ -26,7 +27,7 @@ public class ConcurrentHashSet extends AbstractSet implements java.io.Seri * 触发因子为默认的0.75 */ public ConcurrentHashSet() { - map = new ConcurrentHashMap<>(); + map = new SafeConcurrentHashMap<>(); } /** @@ -36,7 +37,7 @@ public class ConcurrentHashSet extends AbstractSet 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 extends AbstractSet 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 extends AbstractSet 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 extends AbstractSet implements java.io.Seri public ConcurrentHashSet(Iterable iter) { if(iter instanceof Collection) { final Collection collection = (Collection)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); } diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/BasicType.java b/hutool-core/src/main/java/cn/hutool/core/convert/BasicType.java index 5c919eb7d..d62dbb16c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/BasicType.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/BasicType.java @@ -1,7 +1,8 @@ package cn.hutool.core.convert; +import cn.hutool.core.map.SafeConcurrentHashMap; + import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * 基本变量类型的枚举
@@ -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> WRAPPER_PRIMITIVE_MAP = new ConcurrentHashMap<>(8); + public static final Map, Class> WRAPPER_PRIMITIVE_MAP = new SafeConcurrentHashMap<>(8); /** 原始类型为Key,包装类型为Value,例如: int.class =》 Integer.class. */ - public static final Map, Class> PRIMITIVE_WRAPPER_MAP = new ConcurrentHashMap<>(8); + public static final Map, Class> PRIMITIVE_WRAPPER_MAP = new SafeConcurrentHashMap<>(8); static { WRAPPER_PRIMITIVE_MAP.put(Boolean.class, boolean.class); diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java index 45aacb04b..15bce45c7 100755 --- a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java @@ -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)); diff --git a/hutool-core/src/main/java/cn/hutool/core/date/GroupTimeInterval.java b/hutool-core/src/main/java/cn/hutool/core/date/GroupTimeInterval.java index 93367580b..34f5be104 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/GroupTimeInterval.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/GroupTimeInterval.java @@ -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; /** * 分组计时器
@@ -26,7 +26,7 @@ public class GroupTimeInterval implements Serializable { */ public GroupTimeInterval(boolean isNano) { this.isNano = isNano; - groupMap = new ConcurrentHashMap<>(); + groupMap = new SafeConcurrentHashMap<>(); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/FastDateParser.java b/hutool-core/src/main/java/cn/hutool/core/date/format/FastDateParser.java index e0ddfbc84..d89b5b493 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/format/FastDateParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/format/FastDateParser.java @@ -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 getCache(final int field) { synchronized (CACHES) { if (CACHES[field] == null) { - CACHES[field] = new ConcurrentHashMap<>(3); + CACHES[field] = new SafeConcurrentHashMap<>(3); } return CACHES[field]; } diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java b/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java index e4be92723..fb5c2704b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java @@ -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 C_TIME_ZONE_DISPLAY_CACHE = new ConcurrentHashMap<>(7); + private static final ConcurrentMap C_TIME_ZONE_DISPLAY_CACHE = new SafeConcurrentHashMap<>(7); /** *

diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/FormatCache.java b/hutool-core/src/main/java/cn/hutool/core/date/format/FormatCache.java index b79935773..b5716803f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/format/FormatCache.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/format/FormatCache.java @@ -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 { */ static final int NONE = -1; - private final ConcurrentMap cInstanceCache = new ConcurrentHashMap<>(7); + private final ConcurrentMap cInstanceCache = new SafeConcurrentHashMap<>(7); - private static final ConcurrentMap C_DATE_TIME_INSTANCE_CACHE = new ConcurrentHashMap<>(7); + private static final ConcurrentMap C_DATE_TIME_INSTANCE_CACHE = new SafeConcurrentHashMap<>(7); /** * 使用默认的pattern、timezone和locale获得缓存中的实例 diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/GlobalCustomFormat.java b/hutool-core/src/main/java/cn/hutool/core/date/format/GlobalCustomFormat.java index 06ba30f00..df2e295f6 100755 --- a/hutool-core/src/main/java/cn/hutool/core/date/format/GlobalCustomFormat.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/format/GlobalCustomFormat.java @@ -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> 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))); diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/SimpleCache.java b/hutool-core/src/main/java/cn/hutool/core/lang/SimpleCache.java index 67c1c6ece..e4365d87c 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/SimpleCache.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/SimpleCache.java @@ -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 implements Iterable>, Serializabl /** * 写的时候每个key一把锁,降低锁的粒度 */ - protected final Map keyLockMap = new ConcurrentHashMap<>(); + protected final Map keyLockMap = new SafeConcurrentHashMap<>(); /** * 构造,默认使用{@link WeakHashMap}实现缓存自动清理 @@ -102,7 +101,7 @@ public class SimpleCache implements Iterable>, 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 { // 双重检查,防止在竞争锁的过程中已经有其它线程写入 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java b/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java index 5d5c1066e..09de57a9b 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java @@ -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 POOL = new ConcurrentHashMap<>(); + private static final SafeConcurrentHashMap POOL = new SafeConcurrentHashMap<>(); private Singleton() { } @@ -53,7 +52,7 @@ public final class Singleton { */ @SuppressWarnings("unchecked") public static T get(String key, Func0 supplier) { - return (T) MapUtil.computeIfAbsent(POOL, key, (k)-> supplier.callWithRuntimeException()); + return (T) POOL.computeIfAbsent(key, (k)-> supplier.callWithRuntimeException()); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java index e6a28b297..9b9e1e8ba 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java @@ -1464,14 +1464,14 @@ public class MapUtil { } /** - * 方法来自Dubbo,解决使用ConcurrentHashMap.computeIfAbsent导致的死循环问题。
- * issues#2349
+ * 如果 key 对应的 value 不存在,则使用获取 mappingFunction 重新计算后的值,并保存为该 key 的 value,否则返回 value。
+ * 方法来自Dubbo,解决使用ConcurrentHashMap.computeIfAbsent导致的死循环问题。(issues#2349)
* A temporary workaround for Java 8 specific performance issue JDK-8161372 .
* This class should be removed once we drop Java 8 support. * * @see https://bugs.openjdk.java.net/browse/JDK-8161372 */ - public static V computeIfAbsent(Map map, K key, Function mappingFunction) { + public static V computeIfAbsent(Map map, K key, Function mappingFunction) { V value = map.get(key); if(null == value){ map.putIfAbsent(key, mappingFunction.apply(key)); diff --git a/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java b/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java index 5137d7ba9..99e80a877 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java @@ -134,7 +134,7 @@ public class ReferenceConcurrentMap implements ConcurrentMap, Iterab @Override public V computeIfAbsent(K key, Function 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 diff --git a/hutool-core/src/main/java/cn/hutool/core/map/SafeConcurrentHashMap.java b/hutool-core/src/main/java/cn/hutool/core/map/SafeConcurrentHashMap.java new file mode 100644 index 000000000..4eccc900e --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/SafeConcurrentHashMap.java @@ -0,0 +1,74 @@ +package cn.hutool.core.map; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + * 安全的ConcurrentHashMap实现
+ * 此类用于解决在JDK8中调用{@link ConcurrentHashMap#computeIfAbsent(Object, Function)}可能造成的死循环问题。
+ * 方法来自Dubbo,见:issues#2349
+ *

+ * 相关bug见:@see https://bugs.openjdk.java.net/browse/JDK-8161372 + * + * @param 键类型 + * @param 值类型 + */ +public class SafeConcurrentHashMap extends ConcurrentHashMap { + private static final long serialVersionUID = 1L; + + // region == 构造 == + + /** + * 构造,默认初始大小(16) + */ + public SafeConcurrentHashMap() { + super(); + } + + /** + * 构造 + * + * @param initialCapacity 预估初始大小 + */ + public SafeConcurrentHashMap(int initialCapacity) { + super(initialCapacity); + } + + /** + * 构造 + * + * @param m 初始键值对 + */ + public SafeConcurrentHashMap(Map 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 mappingFunction) { + return MapUtil.computeIfAbsent(this, key, mappingFunction); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java b/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java index 5cbbe1332..89369609e 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java @@ -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 extends ReferenceConcurrentMap { * 构造 */ public WeakConcurrentMap() { - this(new ConcurrentHashMap<>()); + this(new SafeConcurrentHashMap<>()); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/text/AntPathMatcher.java b/hutool-core/src/main/java/cn/hutool/core/text/AntPathMatcher.java index 11d07c3ea..5e705b0a3 100755 --- a/hutool-core/src/main/java/cn/hutool/core/text/AntPathMatcher.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/AntPathMatcher.java @@ -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 tokenizedPatternCache = new ConcurrentHashMap<>(256); + private final Map tokenizedPatternCache = new SafeConcurrentHashMap<>(256); - private final Map stringMatcherCache = new ConcurrentHashMap<>(256); + private final Map stringMatcherCache = new SafeConcurrentHashMap<>(256); /** diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java index aeae12b84..d6555bc7e 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java @@ -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> PRIMITIVE_TYPE_NAME_MAP = new ConcurrentHashMap<>(32); + private static final Map> PRIMITIVE_TYPE_NAME_MAP = new SafeConcurrentHashMap<>(32); private static final Map, 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; } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/CreditCodeUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/CreditCodeUtil.java index e1ee0bf26..4e40ea612 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/CreditCodeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/CreditCodeUtil.java @@ -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 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); } diff --git a/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java b/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java index aeb50bf59..8aea4cdc8 100755 --- a/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java +++ b/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java @@ -1,5 +1,6 @@ package cn.hutool.db.dialect; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.util.ClassLoaderUtil; import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; @@ -16,7 +17,6 @@ import cn.hutool.log.StaticLog; import javax.sql.DataSource; import java.sql.Connection; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * 方言工厂类 @@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class DialectFactory implements DriverNamePool{ - private static final Map DIALECT_POOL = new ConcurrentHashMap<>(); + private static final Map DIALECT_POOL = new SafeConcurrentHashMap<>(); private DialectFactory() { } @@ -170,11 +170,7 @@ public class DialectFactory implements DriverNamePool{ // 数据源作为锁的意义在于:不同数据源不会导致阻塞,相同数据源获取方言时可保证互斥 //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (ds) { - dialect = DIALECT_POOL.get(ds); - if(null == dialect) { - dialect = newDialect(ds); - DIALECT_POOL.put(ds, dialect); - } + dialect = DIALECT_POOL.computeIfAbsent(ds, DialectFactory::newDialect); } } return dialect; diff --git a/hutool-db/src/main/java/cn/hutool/db/ds/AbstractDSFactory.java b/hutool-db/src/main/java/cn/hutool/db/ds/AbstractDSFactory.java index a585414c2..960adf717 100644 --- a/hutool-db/src/main/java/cn/hutool/db/ds/AbstractDSFactory.java +++ b/hutool-db/src/main/java/cn/hutool/db/ds/AbstractDSFactory.java @@ -2,6 +2,7 @@ package cn.hutool.db.ds; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.util.StrUtil; import cn.hutool.db.DbRuntimeException; import cn.hutool.db.DbUtil; @@ -12,7 +13,6 @@ import cn.hutool.setting.Setting; import javax.sql.DataSource; import java.util.Collection; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * 抽象数据源工厂
@@ -54,7 +54,7 @@ public abstract class AbstractDSFactory extends DSFactory { DbUtil.setShowSqlGlobal(setting); this.setting = setting; - this.dsMap = new ConcurrentHashMap<>(); + this.dsMap = new SafeConcurrentHashMap<>(); } /** @@ -143,7 +143,6 @@ public abstract class AbstractDSFactory extends DSFactory { DataSourceWrapper ds = dsMap.get(group); if (ds != null) { ds.close(); - //noinspection resource dsMap.remove(group); } } diff --git a/hutool-db/src/main/java/cn/hutool/db/meta/JdbcType.java b/hutool-db/src/main/java/cn/hutool/db/meta/JdbcType.java index 8e4f53150..a275ccab8 100644 --- a/hutool-db/src/main/java/cn/hutool/db/meta/JdbcType.java +++ b/hutool-db/src/main/java/cn/hutool/db/meta/JdbcType.java @@ -1,7 +1,8 @@ package cn.hutool.db.meta; +import cn.hutool.core.map.SafeConcurrentHashMap; + import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * JDBC中字段类型枚举 @@ -56,14 +57,14 @@ public enum JdbcType { /** * 构造 - * + * * @param code {@link java.sql.Types} 中对应的值 */ JdbcType(int code) { this.typeCode = code; } - private static final Map CODE_MAP = new ConcurrentHashMap<>(100, 1); + private static final Map CODE_MAP = new SafeConcurrentHashMap<>(100, 1); static { for (JdbcType type : JdbcType.values()) { CODE_MAP.put(type.typeCode, type); @@ -72,12 +73,12 @@ public enum JdbcType { /** * 通过{@link java.sql.Types}中对应int值找到enum值 - * + * * @param code Jdbc type值 - * @return {@link JdbcType} + * @return {@code JdbcType} */ public static JdbcType valueOf(int code) { return CODE_MAP.get(code); } - + } diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java index 25717bcb7..e8fc3ffa9 100644 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java +++ b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java @@ -1,13 +1,13 @@ package cn.hutool.db.nosql.mongo; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.RuntimeUtil; import cn.hutool.setting.Setting; import java.util.Collection; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * {@link MongoDS}工厂类,用于创建 @@ -24,7 +24,7 @@ public class MongoFactory { /** * 数据源池 */ - private static final Map DS_MAP = new ConcurrentHashMap<>(); + private static final Map DS_MAP = new SafeConcurrentHashMap<>(); // JVM关闭前关闭MongoDB连接 static { @@ -42,14 +42,7 @@ public class MongoFactory { */ public static MongoDS getDS(String host, int port) { final String key = host + ":" + port; - MongoDS ds = DS_MAP.get(key); - if (null == ds) { - // 没有在池中加入之 - ds = new MongoDS(host, port); - DS_MAP.put(key, ds); - } - - return ds; + return DS_MAP.computeIfAbsent(key, (k)->new MongoDS(host, port)); } /** diff --git a/hutool-http/src/main/java/cn/hutool/http/HTMLFilter.java b/hutool-http/src/main/java/cn/hutool/http/HTMLFilter.java index a43e7bc25..ffed78541 100644 --- a/hutool-http/src/main/java/cn/hutool/http/HTMLFilter.java +++ b/hutool-http/src/main/java/cn/hutool/http/HTMLFilter.java @@ -1,6 +1,7 @@ package cn.hutool.http; import cn.hutool.core.lang.Console; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.util.CharUtil; import java.util.ArrayList; @@ -8,7 +9,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -61,8 +61,8 @@ public final class HTMLFilter { private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); // @xxx could grow large... maybe use sesat's ReferenceMap - private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); - private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new SafeConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new SafeConcurrentHashMap<>(); /** * set of allowed html elements, along with allowed attributes for each element diff --git a/hutool-json/src/main/java/cn/hutool/json/serialize/GlobalSerializeMapping.java b/hutool-json/src/main/java/cn/hutool/json/serialize/GlobalSerializeMapping.java index 580bf7ed0..64e21e37b 100644 --- a/hutool-json/src/main/java/cn/hutool/json/serialize/GlobalSerializeMapping.java +++ b/hutool-json/src/main/java/cn/hutool/json/serialize/GlobalSerializeMapping.java @@ -1,5 +1,6 @@ package cn.hutool.json.serialize; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.json.JSON; import java.lang.reflect.Type; @@ -22,8 +23,8 @@ public class GlobalSerializeMapping { private static Map> deserializerMap; static { - serializerMap = new ConcurrentHashMap<>(); - deserializerMap = new ConcurrentHashMap<>(); + serializerMap = new SafeConcurrentHashMap<>(); + deserializerMap = new SafeConcurrentHashMap<>(); final TemporalAccessorSerializer localDateSerializer = new TemporalAccessorSerializer(LocalDate.class); serializerMap.put(LocalDate.class, localDateSerializer); @@ -66,7 +67,7 @@ public class GlobalSerializeMapping { */ synchronized private static void putInternal(Type type, JSONSerializer serializer) { if(null == serializerMap) { - serializerMap = new ConcurrentHashMap<>(); + serializerMap = new SafeConcurrentHashMap<>(); } serializerMap.put(type, serializer); } diff --git a/hutool-log/src/main/java/cn/hutool/log/LogFactory.java b/hutool-log/src/main/java/cn/hutool/log/LogFactory.java index cc6968242..ef43ed7f8 100644 --- a/hutool-log/src/main/java/cn/hutool/log/LogFactory.java +++ b/hutool-log/src/main/java/cn/hutool/log/LogFactory.java @@ -2,13 +2,13 @@ package cn.hutool.log; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.lang.caller.CallerUtil; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.util.ServiceLoaderUtil; import cn.hutool.log.dialect.console.ConsoleLogFactory; import cn.hutool.log.dialect.jdk.JdkLogFactory; import java.net.URL; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * 日志工厂类 @@ -33,7 +33,7 @@ public abstract class LogFactory { */ public LogFactory(String name) { this.name = name; - logCache = new ConcurrentHashMap<>(); + logCache = new SafeConcurrentHashMap<>(); } /** diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java index 7087c14c0..1681d01c2 100755 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java @@ -8,6 +8,7 @@ import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.map.TableMap; import cn.hutool.core.map.multi.RowKeyTable; import cn.hutool.core.map.multi.Table; @@ -44,7 +45,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** @@ -938,7 +938,7 @@ public class ExcelWriter extends ExcelBase { */ public ExcelWriter writeHeadRow(Iterable rowData) { Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); - this.headLocationCache = new ConcurrentHashMap<>(); + this.headLocationCache = new SafeConcurrentHashMap<>(); final Row row = this.sheet.createRow(this.currentRow.getAndIncrement()); int i = 0; Cell cell; diff --git a/hutool-setting/src/main/java/cn/hutool/setting/SettingUtil.java b/hutool-setting/src/main/java/cn/hutool/setting/SettingUtil.java index 5b31d8198..6289f12ed 100644 --- a/hutool-setting/src/main/java/cn/hutool/setting/SettingUtil.java +++ b/hutool-setting/src/main/java/cn/hutool/setting/SettingUtil.java @@ -2,10 +2,10 @@ package cn.hutool.setting; import cn.hutool.core.io.file.FileNameUtil; import cn.hutool.core.io.resource.NoResourceException; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.util.StrUtil; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * Setting工具类
@@ -17,7 +17,7 @@ public class SettingUtil { /** * 配置文件缓存 */ - private static final Map SETTING_MAP = new ConcurrentHashMap<>(); + private static final Map SETTING_MAP = new SafeConcurrentHashMap<>(); /** * 获取当前环境下的配置文件
diff --git a/hutool-setting/src/main/java/cn/hutool/setting/dialect/PropsUtil.java b/hutool-setting/src/main/java/cn/hutool/setting/dialect/PropsUtil.java index a54f8a9d6..147ecc504 100644 --- a/hutool-setting/src/main/java/cn/hutool/setting/dialect/PropsUtil.java +++ b/hutool-setting/src/main/java/cn/hutool/setting/dialect/PropsUtil.java @@ -2,10 +2,10 @@ package cn.hutool.setting.dialect; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.resource.NoResourceException; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.util.StrUtil; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * Props工具类
@@ -19,7 +19,7 @@ public class PropsUtil { /** * 配置文件缓存 */ - private static final Map propsMap = new ConcurrentHashMap<>(); + private static final Map propsMap = new SafeConcurrentHashMap<>(); /** * 获取当前环境下的配置文件
diff --git a/hutool-setting/src/main/java/cn/hutool/setting/profile/Profile.java b/hutool-setting/src/main/java/cn/hutool/setting/profile/Profile.java index c66c82b8f..b325bd207 100755 --- a/hutool-setting/src/main/java/cn/hutool/setting/profile/Profile.java +++ b/hutool-setting/src/main/java/cn/hutool/setting/profile/Profile.java @@ -1,13 +1,13 @@ package cn.hutool.setting.profile; import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.SafeConcurrentHashMap; import cn.hutool.core.util.StrUtil; import cn.hutool.setting.Setting; import java.io.Serializable; import java.nio.charset.Charset; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * Profile可以让我们定义一系列的配置信息,然后指定其激活条件。
@@ -19,7 +19,7 @@ import java.util.concurrent.ConcurrentHashMap; *

  • develop =》 ${classpath}/develop/db.setting
  • *
  • production =》 ${classpath}/production/db.setting
  • * - * + * * @author Looly * */ @@ -36,7 +36,7 @@ public class Profile implements Serializable { /** 是否使用变量 */ private boolean useVar; /** 配置文件缓存 */ - private final Map settingMap = new ConcurrentHashMap<>(); + private final Map settingMap = new SafeConcurrentHashMap<>(); // -------------------------------------------------------------------------------- Constructor start /** @@ -48,7 +48,7 @@ public class Profile implements Serializable { /** * 构造,编码UTF-8,不使用变量 - * + * * @param profile 环境 */ public Profile(String profile) { @@ -57,7 +57,7 @@ public class Profile implements Serializable { /** * 构造 - * + * * @param profile 环境 * @param charset 编码 * @param useVar 是否使用变量 @@ -71,7 +71,7 @@ public class Profile implements Serializable { /** * 获取当前环境下的配置文件 - * + * * @param name 文件名,如果没有扩展名,默认为.setting * @return 当前环境下配置文件 */ @@ -87,7 +87,7 @@ public class Profile implements Serializable { /** * 设置环境 - * + * * @param profile 环境 * @return 自身 */ @@ -98,7 +98,7 @@ public class Profile implements Serializable { /** * 设置编码 - * + * * @param charset 编码 * @return 自身 */ @@ -109,7 +109,7 @@ public class Profile implements Serializable { /** * 设置是否使用变量 - * + * * @param useVar 变量 * @return 自身 */ @@ -120,7 +120,7 @@ public class Profile implements Serializable { /** * 清空所有环境的配置文件 - * + * * @return 自身 */ public Profile clear() { @@ -131,7 +131,7 @@ public class Profile implements Serializable { // -------------------------------------------------------------------------------- Private method start /** * 修正文件名 - * + * * @param name 文件名 * @return 修正后的文件名 */