diff --git a/CHANGELOG.md b/CHANGELOG.md index ec5c7c1d9..3b7f33a87 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,12 @@ * 【json 】 新增JSONParser * 【json 】 JSON新增在解析时的过滤方法(issue#I52O85@Gitee) * 【core 】 添加ArrayUtil.distinct、CollUtil.distinct重载(issue#2256@Github) +* 【core 】 添加TransMap、FuncMap、ReferenceConcurrentMap、WeakConcurrentMap ### 🐞Bug修复 * 【core 】 修复StrUtil.firstNonX非static问题(issue#2257@Github) +* 【core 】 修复SimpleCache线程安全问题 +* 【core 】 修复ClassLoaderUtil中可能的关联ClassLoader错位问题 ------------------------------------------------------------------------------------------------------------- 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 b41d030c2..fa29e1d94 100644 --- 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,7 @@ package cn.hutool.core.bean; -import cn.hutool.core.lang.SimpleCache; import cn.hutool.core.lang.func.Func0; +import cn.hutool.core.map.WeakConcurrentMap; /** * Bean属性缓存
@@ -12,7 +12,7 @@ import cn.hutool.core.lang.func.Func0; public enum BeanDescCache { INSTANCE; - private final SimpleCache, BeanDesc> bdCache = new SimpleCache<>(); + private final WeakConcurrentMap, BeanDesc> bdCache = new WeakConcurrentMap<>(); /** * 获得属性名和{@link BeanDesc}Map映射 @@ -23,7 +23,7 @@ public enum BeanDescCache { * @since 5.4.2 */ public BeanDesc getBeanDesc(Class beanClass, Func0 supplier) { - return bdCache.get(beanClass, supplier); + return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException()); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java index 7a950ba0c..63fc224d5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java @@ -1,7 +1,8 @@ package cn.hutool.core.bean; -import cn.hutool.core.lang.SimpleCache; import cn.hutool.core.lang.func.Func0; +import cn.hutool.core.map.ReferenceConcurrentMap; +import cn.hutool.core.map.WeakConcurrentMap; import java.beans.PropertyDescriptor; import java.util.Map; @@ -15,8 +16,8 @@ import java.util.Map; public enum BeanInfoCache { INSTANCE; - private final SimpleCache, Map> pdCache = new SimpleCache<>(); - private final SimpleCache, Map> ignoreCasePdCache = new SimpleCache<>(); + private final WeakConcurrentMap, Map> pdCache = new WeakConcurrentMap<>(); + private final WeakConcurrentMap, Map> ignoreCasePdCache = new WeakConcurrentMap<>(); /** * 获得属性名和{@link PropertyDescriptor}Map映射 @@ -42,7 +43,7 @@ public enum BeanInfoCache { Class beanClass, boolean ignoreCase, Func0> supplier) { - return getCache(ignoreCase).get(beanClass, supplier); + return getCache(ignoreCase).computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException()); } /** @@ -70,10 +71,10 @@ public enum BeanInfoCache { * 根据是否忽略字段名的大小写,返回不用Cache对象 * * @param ignoreCase 是否忽略大小写 - * @return SimpleCache + * @return {@link ReferenceConcurrentMap} * @since 5.4.1 */ - private SimpleCache, Map> getCache(boolean ignoreCase) { + private ReferenceConcurrentMap, Map> getCache(boolean ignoreCase) { return ignoreCase ? ignoreCasePdCache : pdCache; } } diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java index ba9e2db96..d73a458ae 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java @@ -3,8 +3,8 @@ package cn.hutool.core.convert.impl; import cn.hutool.core.convert.AbstractConverter; import cn.hutool.core.convert.ConvertException; import cn.hutool.core.lang.EnumItem; -import cn.hutool.core.lang.SimpleCache; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.ModifierUtil; @@ -25,7 +25,7 @@ import java.util.stream.Collectors; public class EnumConverter extends AbstractConverter { private static final long serialVersionUID = 1L; - private static final SimpleCache, Map, Method>> VALUE_OF_METHOD_CACHE = new SimpleCache<>(); + private static final WeakConcurrentMap, Map, Method>> VALUE_OF_METHOD_CACHE = new WeakConcurrentMap<>(); private final Class enumClass; @@ -132,7 +132,7 @@ public class EnumConverter extends AbstractConverter { * @return 转换方法map,key为方法参数类型,value为方法 */ private static Map, Method> getMethodMap(Class enumClass) { - return VALUE_OF_METHOD_CACHE.get(enumClass, () -> Arrays.stream(enumClass.getMethods()) + return VALUE_OF_METHOD_CACHE.computeIfAbsent(enumClass, (key) -> Arrays.stream(enumClass.getMethods()) .filter(ModifierUtil::isStatic) .filter(m -> m.getReturnType() == enumClass) .filter(m -> m.getParameterCount() == 1) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/PatternPool.java b/hutool-core/src/main/java/cn/hutool/core/lang/PatternPool.java index b22563f10..93501ea24 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/PatternPool.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/PatternPool.java @@ -1,5 +1,7 @@ package cn.hutool.core.lang; +import cn.hutool.core.map.WeakConcurrentMap; + import java.util.regex.Pattern; /** @@ -174,7 +176,7 @@ public class PatternPool { /** * Pattern池 */ - private static final SimpleCache POOL = new SimpleCache<>(); + private static final WeakConcurrentMap POOL = new WeakConcurrentMap<>(); /** * 先从Pattern池中查找正则对应的{@link Pattern},找不到则编译正则表达式并入池。 @@ -195,7 +197,7 @@ public class PatternPool { */ public static Pattern get(String regex, int flags) { final RegexWithFlag regexWithFlag = new RegexWithFlag(regex, flags); - return POOL.get(regexWithFlag, ()-> Pattern.compile(regex, flags)); + return POOL.computeIfAbsent(regexWithFlag, (key)-> Pattern.compile(regex, flags)); } /** 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 027026093..5bef6a15f 100644 --- 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,6 +4,7 @@ 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.WeakConcurrentMap; import java.io.Serializable; import java.util.Iterator; @@ -11,12 +12,13 @@ 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; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Predicate; /** - * 简单缓存,无超时实现,默认使用{@link WeakHashMap}实现缓存自动清理 + * 简单缓存,无超时实现,默认使用{@link WeakConcurrentMap}实现缓存自动清理 * * @param 键类型 * @param 值类型 @@ -30,7 +32,7 @@ public class SimpleCache implements Iterable>, Serializabl */ private final Map, V> rawMap; // 乐观读写锁 - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); /** * 写的时候每个key一把锁,降低锁的粒度 */ @@ -40,7 +42,7 @@ public class SimpleCache implements Iterable>, Serializabl * 构造,默认使用{@link WeakHashMap}实现缓存自动清理 */ public SimpleCache() { - this(new WeakHashMap<>()); + this(new WeakConcurrentMap<>()); } /** @@ -94,6 +96,9 @@ public class SimpleCache implements Iterable>, Serializabl */ public V get(K key, Predicate validPredicate, Func0 supplier) { V v = get(key); + if((null != validPredicate && false == validPredicate.test(v))){ + v = null; + } if (null == v && null != supplier) { //每个key单独获取一把锁,降低锁的粒度提高并发能力,see pr#1385@Github final Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock()); 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 f74462ea7..a4f210100 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java @@ -6,7 +6,7 @@ import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; /** * 单例类
@@ -16,7 +16,7 @@ import java.util.HashMap; */ public final class Singleton { - private static final SimpleCache POOL = new SimpleCache<>(new HashMap<>()); + private static final ConcurrentHashMap POOL = new ConcurrentHashMap<>(); private Singleton() { } @@ -50,7 +50,7 @@ public final class Singleton { */ @SuppressWarnings("unchecked") public static T get(String key, Func0 supplier) { - return (T) POOL.get(key, supplier::call); + return (T) POOL.computeIfAbsent(key, (k)-> supplier.callWithRuntimeException()); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java index d9b3cbc15..da4bb61f9 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java @@ -1,7 +1,7 @@ package cn.hutool.core.lang.func; import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; @@ -18,7 +18,7 @@ import java.lang.invoke.SerializedLambda; */ public class LambdaUtil { - private static final SimpleCache cache = new SimpleCache<>(); + private static final WeakConcurrentMap cache = new WeakConcurrentMap<>(); /** * 通过对象的方法或类的静态方法引用,获取lambda实现类 @@ -202,7 +202,7 @@ public class LambdaUtil { * @return 返回解析后的结果 */ private static SerializedLambda _resolve(Serializable func) { - return cache.get(func.getClass().getName(), () -> ReflectUtil.invoke(func, "writeReplace")); + return cache.computeIfAbsent(func.getClass().getName(), (key) -> ReflectUtil.invoke(func, "writeReplace")); } //endregion } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/intern/WeakInterner.java b/hutool-core/src/main/java/cn/hutool/core/lang/intern/WeakInterner.java index a6b741d83..7f9b8615d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/intern/WeakInterner.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/intern/WeakInterner.java @@ -1,6 +1,6 @@ package cn.hutool.core.lang.intern; -import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.map.WeakConcurrentMap; /** * 使用WeakHashMap(线程安全)存储对象的规范化对象,注意此对象需单例使用!
@@ -10,13 +10,13 @@ import cn.hutool.core.lang.SimpleCache; */ public class WeakInterner implements Interner{ - private final SimpleCache cache = new SimpleCache<>(); + private final WeakConcurrentMap cache = new WeakConcurrentMap<>(); @Override public T intern(T sample) { if(null == sample){ return null; } - return cache.get(sample, ()->sample); + return cache.computeIfAbsent(sample, (key)->sample); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/reflect/ActualTypeMapperPool.java b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/ActualTypeMapperPool.java index 5bb5f6d5f..ea213ec1c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/reflect/ActualTypeMapperPool.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/ActualTypeMapperPool.java @@ -1,7 +1,7 @@ package cn.hutool.core.lang.reflect; import cn.hutool.core.convert.Convert; -import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.util.TypeUtil; import java.lang.reflect.ParameterizedType; @@ -18,7 +18,7 @@ import java.util.Map; */ public class ActualTypeMapperPool { - private static final SimpleCache> CACHE = new SimpleCache<>(); + private static final WeakConcurrentMap> CACHE = new WeakConcurrentMap<>(); /** * 获取泛型变量和泛型实际类型的对应关系Map @@ -27,7 +27,7 @@ public class ActualTypeMapperPool { * @return 泛型对应关系Map */ public static Map get(Type type) { - return CACHE.get(type, () -> createTypeMap(type)); + return CACHE.computeIfAbsent(type, (key) -> createTypeMap(type)); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/map/CustomKeyMap.java b/hutool-core/src/main/java/cn/hutool/core/map/CustomKeyMap.java index 04ed9d371..701f0d13e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/CustomKeyMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/CustomKeyMap.java @@ -1,7 +1,6 @@ package cn.hutool.core.map; import java.util.Map; -import java.util.function.BiFunction; /** * 自定义键的Map,默认HashMap实现 @@ -11,7 +10,7 @@ import java.util.function.BiFunction; * @author Looly * @since 4.0.7 */ -public abstract class CustomKeyMap extends MapWrapper { +public abstract class CustomKeyMap extends TransMap { private static final long serialVersionUID = 4043263744224569870L; /** @@ -26,78 +25,8 @@ public abstract class CustomKeyMap extends MapWrapper { } @Override - public V get(Object key) { - return super.get(customKey(key)); - } - - @SuppressWarnings("unchecked") - @Override - public V put(K key, V value) { - return super.put((K) customKey(key), value); - } - - @Override - public void putAll(Map m) { - m.forEach(this::put); - } - - @Override - public boolean containsKey(Object key) { - return super.containsKey(customKey(key)); - } - - @Override - public V remove(Object key) { - return super.remove(customKey(key)); - } - - @Override - public boolean remove(Object key, Object value) { - return super.remove(customKey(key), value); - } - - @Override - public boolean replace(K key, V oldValue, V newValue) { + protected V customValue(Object value) { //noinspection unchecked - return super.replace((K) customKey(key), oldValue, newValue); + return (V)value; } - - @Override - public V replace(K key, V value) { - //noinspection unchecked - return super.replace((K) customKey(key), value); - } - - //---------------------------------------------------------------------------- Override default methods start - @Override - public V getOrDefault(Object key, V defaultValue) { - return super.getOrDefault(customKey(key), defaultValue); - } - - @Override - public V computeIfPresent(K key, BiFunction remappingFunction) { - //noinspection unchecked - return super.computeIfPresent((K) customKey(key), remappingFunction); - } - - @Override - public V compute(K key, BiFunction remappingFunction) { - //noinspection unchecked - return super.compute((K) customKey(key), remappingFunction); - } - - @Override - public V merge(K key, V value, BiFunction remappingFunction) { - //noinspection unchecked - return super.merge((K) customKey(key), value, remappingFunction); - } - //---------------------------------------------------------------------------- Override default methods end - - /** - * 自定义键 - * - * @param key KEY - * @return 自定义KEY - */ - protected abstract Object customKey(Object key); } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java b/hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java index 0cc468d61..91a6f231a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java @@ -38,10 +38,11 @@ public class FuncKeyMap extends CustomKeyMap { * @return 驼峰Key */ @Override - protected Object customKey(Object key) { + protected K customKey(Object key) { if (null != this.keyFunc) { return keyFunc.apply(key); } - return key; + //noinspection unchecked + return (K)key; } } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/FuncMap.java b/hutool-core/src/main/java/cn/hutool/core/map/FuncMap.java new file mode 100755 index 000000000..7007a2822 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/FuncMap.java @@ -0,0 +1,73 @@ +package cn.hutool.core.map; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * 自定义键值函数风格的Map + * + * @param 键类型 + * @param 值类型 + * @author Looly + * @since 5.8.0 + */ +public class FuncMap extends TransMap { + private static final long serialVersionUID = 1L; + + private final Function keyFunc; + private final Function valueFunc; + + // ------------------------------------------------------------------------- Constructor start + + /** + * 构造
+ * 注意提供的Map中不能有键值对,否则可能导致自定义key失效 + * + * @param mapFactory Map,提供的空map + * @param keyFunc 自定义KEY的函数 + * @param valueFunc 自定义value函数 + */ + public FuncMap(Supplier> mapFactory, Function keyFunc, Function valueFunc) { + this(mapFactory.get(), keyFunc, valueFunc); + } + + /** + * 构造
+ * 注意提供的Map中不能有键值对,否则可能导致自定义key失效 + * + * @param emptyMap Map,提供的空map + * @param keyFunc 自定义KEY的函数 + * @param valueFunc 自定义value函数 + */ + public FuncMap(Map emptyMap, Function keyFunc, Function valueFunc) { + super(emptyMap); + this.keyFunc = keyFunc; + this.valueFunc = valueFunc; + } + // ------------------------------------------------------------------------- Constructor end + + /** + * 根据函数自定义键 + * + * @param key KEY + * @return 驼峰Key + */ + @Override + protected K customKey(Object key) { + if (null != this.keyFunc) { + return keyFunc.apply(key); + } + //noinspection unchecked + return (K) key; + } + + @Override + protected V customValue(Object value) { + if (null != this.valueFunc) { + return valueFunc.apply(value); + } + //noinspection unchecked + return (V) value; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java index 3c1ac5936..c54a581f2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java @@ -2,6 +2,9 @@ package cn.hutool.core.map; import cn.hutool.core.util.ObjectUtil; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collection; import java.util.Iterator; @@ -11,6 +14,7 @@ import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; /** * Map包装类,通过包装一个已有Map实现特定功能。例如自定义Key的规则或Value规则 @@ -34,6 +38,17 @@ public class MapWrapper implements Map, Iterable>, S private Map raw; + /** + * 构造
+ * 通过传入一个Map从而确定Map的类型,子类需创建一个空的Map,而非传入一个已有Map,否则值可能会被修改 + * + * @param mapFactory 空Map创建工厂 + * @since 5.8.0 + */ + public MapWrapper(Supplier> mapFactory) { + this(mapFactory.get()); + } + /** * 构造 * @@ -199,11 +214,23 @@ public class MapWrapper implements Map, Iterable>, S @Override public MapWrapper clone() throws CloneNotSupportedException { - @SuppressWarnings("unchecked") - final MapWrapper clone = (MapWrapper) super.clone(); + @SuppressWarnings("unchecked") final MapWrapper clone = (MapWrapper) super.clone(); clone.raw = ObjectUtil.clone(raw); return clone; } //---------------------------------------------------------------------------- Override default methods end + + // region 序列化与反序列化重写 + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(this.raw); + } + + @SuppressWarnings("unchecked") + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + raw = (Map) in.readObject(); + } + // endregion } 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 new file mode 100755 index 000000000..a976ca30f --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java @@ -0,0 +1,322 @@ +package cn.hutool.core.map; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.func.Func0; +import cn.hutool.core.lang.func.VoidFunc1; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReferenceUtil; + +import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 线程安全的ReferenceMap实现
+ * 参考:jdk.management.resource.internal.WeakKeyConcurrentHashMap + * + * @param 键类型 + * @param 值类型 + * @author looly + * @since 5.8.0 + */ +public class ReferenceConcurrentMap implements ConcurrentMap, Iterable>, Serializable { + + final ConcurrentMap, V> raw; + private final ReferenceQueue lastQueue; + private final ReferenceUtil.ReferenceType keyType; + /** + * 回收监听 + */ + private VoidFunc1> purgeListener; + + // region 构造 + + /** + * 构造 + * + * @param raw {@link ConcurrentMap}实现 + * @param referenceType Reference类型 + */ + public ReferenceConcurrentMap(ConcurrentMap, V> raw, ReferenceUtil.ReferenceType referenceType) { + this.raw = raw; + this.keyType = referenceType; + lastQueue = new ReferenceQueue<>(); + } + // endregion + + /** + * 设置对象回收清除监听 + * + * @param purgeListener 监听函数 + */ + public void setPurgeListener(VoidFunc1> purgeListener) { + this.purgeListener = purgeListener; + } + + @Override + public int size() { + this.purgeStaleKeys(); + return this.raw.size(); + } + + @Override + public boolean isEmpty() { + return 0 == size(); + } + + @Override + public V get(Object key) { + this.purgeStaleKeys(); + //noinspection unchecked + return this.raw.get(ofKey((K) key, null)); + } + + @Override + public boolean containsKey(Object key) { + this.purgeStaleKeys(); + //noinspection unchecked + return this.raw.containsKey(ofKey((K) key, null)); + } + + @Override + public boolean containsValue(Object value) { + this.purgeStaleKeys(); + return this.raw.containsValue(value); + } + + @Override + public V put(K key, V value) { + this.purgeStaleKeys(); + return this.raw.put(ofKey(key, this.lastQueue), value); + } + + @Override + public V putIfAbsent(K key, V value) { + this.purgeStaleKeys(); + return this.raw.putIfAbsent(ofKey(key, this.lastQueue), value); + } + + @Override + public void putAll(Map m) { + m.forEach(this::put); + } + + @Override + public V replace(K key, V value) { + this.purgeStaleKeys(); + return this.raw.replace(ofKey(key, this.lastQueue), value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + this.purgeStaleKeys(); + return this.raw.replace(ofKey(key, this.lastQueue), oldValue, newValue); + } + + @Override + public void replaceAll(BiFunction function) { + this.purgeStaleKeys(); + this.raw.replaceAll((kWeakKey, value) -> function.apply(kWeakKey.get(), value)); + } + + @Override + public V computeIfAbsent(K key, Function mappingFunction) { + this.purgeStaleKeys(); + return this.raw.computeIfAbsent(ofKey(key, this.lastQueue), kWeakKey -> mappingFunction.apply(key)); + } + + @Override + public V computeIfPresent(K key, BiFunction remappingFunction) { + this.purgeStaleKeys(); + return this.raw.computeIfPresent(ofKey(key, this.lastQueue), (kWeakKey, value) -> remappingFunction.apply(key, value)); + } + + /** + * 从缓存中获得对象,当对象不在缓存中或已经过期返回Func0回调产生的对象 + * + * @param key 键 + * @param supplier 如果不存在回调方法,用于生产值对象 + * @return 值对象 + */ + public V computeIfAbsent(K key, Func0 supplier) { + return computeIfAbsent(key, (keyParam) -> supplier.callWithRuntimeException()); + } + + @Override + public V remove(Object key) { + this.purgeStaleKeys(); + //noinspection unchecked + return this.raw.remove(ofKey((K) key, null)); + } + + @Override + public boolean remove(Object key, Object value) { + this.purgeStaleKeys(); + //noinspection unchecked + return this.raw.remove(ofKey((K) key, null), value); + } + + @Override + public void clear() { + this.raw.clear(); + //noinspection StatementWithEmptyBody + while (lastQueue.poll() != null) ; + } + + @Override + public Set keySet() { + // TODO 非高效方式的set转换,应该返回一个view + final Collection trans = CollUtil.trans(this.raw.keySet(), (reference) -> null == reference ? null : reference.get()); + return new HashSet<>(trans); + } + + @Override + public Collection values() { + this.purgeStaleKeys(); + return this.raw.values(); + } + + @Override + public Set> entrySet() { + this.purgeStaleKeys(); + return this.raw.entrySet().stream() + .map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey().get(), entry.getValue())) + .collect(Collectors.toSet()); + } + + @Override + public void forEach(BiConsumer action) { + this.purgeStaleKeys(); + this.raw.forEach((key, value)-> action.accept(key.get(), value)); + } + + @Override + public Iterator> iterator() { + return entrySet().iterator(); + } + + @Override + public V compute(K key, BiFunction remappingFunction) { + this.purgeStaleKeys(); + return this.raw.compute(ofKey(key, this.lastQueue), (kWeakKey, value) -> remappingFunction.apply(key, value)); + } + + @Override + public V merge(K key, V value, BiFunction remappingFunction) { + this.purgeStaleKeys(); + return this.raw.merge(ofKey(key, this.lastQueue), value, remappingFunction); + } + + /** + * 清除被回收的键 + */ + private void purgeStaleKeys() { + Reference reference; + while ((reference = this.lastQueue.poll()) != null) { + this.raw.remove(reference); + if (null != purgeListener) { + purgeListener.callWithRuntimeException(reference); + } + } + } + + /** + * 根据Reference类型构建key对应的{@link Reference} + * + * @param key 键 + * @param queue {@link ReferenceQueue} + * @return {@link Reference} + */ + private Reference ofKey(K key, ReferenceQueue queue) { + switch (keyType) { + case WEAK: + return new WeakKey<>(key, queue); + case SOFT: + return new SoftKey<>(key, queue); + } + throw new IllegalArgumentException("Unsupported key type: " + keyType); + } + + /** + * 弱键 + * + * @param 键类型 + */ + private static class WeakKey extends WeakReference { + private final int hashCode; + + /** + * 构造 + * + * @param key 原始Key,不能为{@code null} + * @param queue {@link ReferenceQueue} + */ + WeakKey(K key, ReferenceQueue queue) { + super(key, queue); + hashCode = key.hashCode(); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof WeakKey) { + return ObjectUtil.equals(((WeakKey) other).get(), get()); + } + return false; + } + } + + /** + * 弱键 + * + * @param 键类型 + */ + private static class SoftKey extends SoftReference { + private final int hashCode; + + /** + * 构造 + * + * @param key 原始Key,不能为{@code null} + * @param queue {@link ReferenceQueue} + */ + SoftKey(K key, ReferenceQueue queue) { + super(key, queue); + hashCode = key.hashCode(); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof SoftKey) { + return ObjectUtil.equals(((SoftKey) other).get(), get()); + } + return false; + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java b/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java new file mode 100755 index 000000000..2fa508c53 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java @@ -0,0 +1,118 @@ +package cn.hutool.core.map; + +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +/** + * 自定义键和值转换的的Map
+ * 继承此类后,通过实现{@link #customKey(Object)}和{@link #customValue(Object)},按照给定规则加入到map或获取值。 + * + * @param 键类型 + * @param 值类型 + * @author Looly + * @since 5.8.0 + */ +public abstract class TransMap extends MapWrapper { + private static final long serialVersionUID = 1L; + + /** + * 构造
+ * 通过传入一个Map从而确定Map的类型,子类需创建一个空的Map,而非传入一个已有Map,否则值可能会被修改 + * + * @param mapFactory 空Map创建工厂 + * @since 5.8.0 + */ + public TransMap(Supplier> mapFactory) { + super(mapFactory); + } + + /** + * 构造
+ * 通过传入一个Map从而确定Map的类型,子类需创建一个空的Map,而非传入一个已有Map,否则值可能会被修改 + * + * @param emptyMap Map 被包装的Map,必须为空Map,否则自定义key会无效 + * @since 3.1.2 + */ + public TransMap(Map emptyMap) { + super(emptyMap); + } + + @Override + public V get(Object key) { + return super.get(customKey(key)); + } + + @Override + public V put(K key, V value) { + return super.put(customKey(key), customValue(value)); + } + + @Override + public void putAll(Map m) { + m.forEach(this::put); + } + + @Override + public boolean containsKey(Object key) { + return super.containsKey(customKey(key)); + } + + @Override + public V remove(Object key) { + return super.remove(customKey(key)); + } + + @Override + public boolean remove(Object key, Object value) { + return super.remove(customKey(key), customValue(value)); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + return super.replace(customKey(key), customValue(oldValue), customValue(values())); + } + + @Override + public V replace(K key, V value) { + return super.replace(customKey(key), customValue(value)); + } + + //---------------------------------------------------------------------------- Override default methods start + @Override + public V getOrDefault(Object key, V defaultValue) { + return super.getOrDefault(customKey(key), customValue(defaultValue)); + } + + @Override + public V computeIfPresent(K key, BiFunction remappingFunction) { + return super.computeIfPresent(customKey(key), (k, v) -> remappingFunction.apply(customKey(k), customValue(v))); + } + + @Override + public V compute(K key, BiFunction remappingFunction) { + return super.compute(customKey(key), (k, v) -> remappingFunction.apply(customKey(k), customValue(v))); + } + + @Override + public V merge(K key, V value, BiFunction remappingFunction) { + return super.merge(customKey(key), customValue(value), (v1, v2) -> remappingFunction.apply(customValue(v1), customValue(v2))); + } + //---------------------------------------------------------------------------- Override default methods end + + /** + * 自定义键 + * + * @param key KEY + * @return 自定义KEY + */ + protected abstract K customKey(Object key); + + /** + * 自定义值 + * + * @param value 值 + * @return 自定义值 + */ + protected abstract V customValue(Object value); +} 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 new file mode 100755 index 000000000..5cbbe1332 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java @@ -0,0 +1,35 @@ +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; + +/** + * 线程安全的WeakMap实现
+ * 参考:jdk.management.resource.internal.WeakKeyConcurrentHashMap + * + * @param 键类型 + * @param 值类型 + * @author looly + * @since 5.8.0 + */ +public class WeakConcurrentMap extends ReferenceConcurrentMap { + + /** + * 构造 + */ + public WeakConcurrentMap() { + this(new ConcurrentHashMap<>()); + } + + /** + * 构造 + * + * @param raw {@link ConcurrentMap}实现 + */ + public WeakConcurrentMap(ConcurrentMap, V> raw) { + super(raw, ReferenceUtil.ReferenceType.WEAK); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/thread/lock/NoLock.java b/hutool-core/src/main/java/cn/hutool/core/thread/lock/NoLock.java index 7ef4b9d46..09d6b8648 100644 --- a/hutool-core/src/main/java/cn/hutool/core/thread/lock/NoLock.java +++ b/hutool-core/src/main/java/cn/hutool/core/thread/lock/NoLock.java @@ -12,6 +12,8 @@ import java.util.concurrent.locks.Lock; */ public class NoLock implements Lock{ + public static NoLock INSTANCE = new NoLock(); + @Override public void lock() { } diff --git a/hutool-core/src/main/java/cn/hutool/core/thread/lock/NoReadWriteLock.java b/hutool-core/src/main/java/cn/hutool/core/thread/lock/NoReadWriteLock.java new file mode 100755 index 000000000..ce84885df --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/thread/lock/NoReadWriteLock.java @@ -0,0 +1,22 @@ +package cn.hutool.core.thread.lock; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; + +/** + * 无锁的读写锁实现 + * + * @author looly + * @since 5.8.0 + */ +public class NoReadWriteLock implements ReadWriteLock { + @Override + public Lock readLock() { + return NoLock.INSTANCE; + } + + @Override + public Lock writeLock() { + return NoLock.INSTANCE; + } +} 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 ef13c4f2e..2300fe107 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java @@ -4,7 +4,8 @@ import cn.hutool.core.convert.BasicType; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.JarClassLoader; -import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.lang.Pair; +import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.text.CharPool; import java.io.File; @@ -49,7 +50,7 @@ public class ClassLoaderUtil { * 原始类型名和其class对应表,例如:int =》 int.class */ private static final Map> PRIMITIVE_TYPE_NAME_MAP = new ConcurrentHashMap<>(32); - private static final SimpleCache> CLASS_CACHE = new SimpleCache<>(); + private static final WeakConcurrentMap, Class> CLASS_CACHE = new WeakConcurrentMap<>(); static { List> primitiveTypes = new ArrayList<>(32); @@ -179,7 +180,7 @@ public class ClassLoaderUtil { * * * @param name 类名 - * @param classLoader {@link ClassLoader},{@code null} 则使用系统默认ClassLoader + * @param classLoader {@link ClassLoader},{@code null} 则使用{@link #getClassLoader()}获取 * @param isInitialized 是否初始化类(调用static模块内容和初始化static属性) * @return 类名对应的类 * @throws UtilException 包装{@link ClassNotFoundException},没有类名对应的类时抛出此异常 @@ -189,49 +190,18 @@ public class ClassLoaderUtil { // 自动将包名中的"/"替换为"." name = name.replace(CharPool.SLASH, CharPool.DOT); + if(null == classLoader){ + classLoader = getClassLoader(); + } // 加载原始类型和缓存中的类 Class clazz = loadPrimitiveClass(name); if (clazz == null) { - clazz = CLASS_CACHE.get(name); + final String finalName = name; + final ClassLoader finalClassLoader = classLoader; + clazz = CLASS_CACHE.computeIfAbsent(Pair.of(name, classLoader), (key)-> doLoadClass(finalName, finalClassLoader, isInitialized)); } - if (clazz != null) { - return clazz; - } - - if (name.endsWith(ARRAY_SUFFIX)) { - // 对象数组"java.lang.String[]"风格 - final String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); - final Class elementClass = loadClass(elementClassName, classLoader, isInitialized); - clazz = Array.newInstance(elementClass, 0).getClass(); - } else if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) { - // "[Ljava.lang.String;" 风格 - final String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1); - final Class elementClass = loadClass(elementName, classLoader, isInitialized); - clazz = Array.newInstance(elementClass, 0).getClass(); - } else if (name.startsWith(INTERNAL_ARRAY_PREFIX)) { - // "[[I" 或 "[[Ljava.lang.String;" 风格 - final String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length()); - final Class elementClass = loadClass(elementName, classLoader, isInitialized); - clazz = Array.newInstance(elementClass, 0).getClass(); - } else { - // 加载普通类 - if (null == classLoader) { - classLoader = getClassLoader(); - } - try { - clazz = Class.forName(name, isInitialized, classLoader); - } catch (ClassNotFoundException ex) { - // 尝试获取内部类,例如java.lang.Thread.State =》java.lang.Thread$State - clazz = tryLoadInnerClass(name, classLoader, isInitialized); - if (null == clazz) { - throw new UtilException(ex); - } - } - } - - // 加入缓存并返回 - return CLASS_CACHE.put(name, clazz); + return clazz; } /** @@ -311,6 +281,47 @@ public class ClassLoaderUtil { } // ----------------------------------------------------------------------------------- Private method start + /** + * 加载非原始类类,无缓存 + * @param name 类名 + * @param classLoader {@link ClassLoader} + * @param isInitialized 是否初始化 + * @return 类 + */ + private static Class doLoadClass(String name, ClassLoader classLoader, boolean isInitialized){ + Class clazz; + if (name.endsWith(ARRAY_SUFFIX)) { + // 对象数组"java.lang.String[]"风格 + final String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); + final Class elementClass = loadClass(elementClassName, classLoader, isInitialized); + clazz = Array.newInstance(elementClass, 0).getClass(); + } else if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) { + // "[Ljava.lang.String;" 风格 + final String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1); + final Class elementClass = loadClass(elementName, classLoader, isInitialized); + clazz = Array.newInstance(elementClass, 0).getClass(); + } else if (name.startsWith(INTERNAL_ARRAY_PREFIX)) { + // "[[I" 或 "[[Ljava.lang.String;" 风格 + final String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length()); + final Class elementClass = loadClass(elementName, classLoader, isInitialized); + clazz = Array.newInstance(elementClass, 0).getClass(); + } else { + // 加载普通类 + if (null == classLoader) { + classLoader = getClassLoader(); + } + try { + clazz = Class.forName(name, isInitialized, classLoader); + } catch (ClassNotFoundException ex) { + // 尝试获取内部类,例如java.lang.Thread.State =》java.lang.Thread$State + clazz = tryLoadInnerClass(name, classLoader, isInitialized); + if (null == clazz) { + throw new UtilException(ex); + } + } + } + return clazz; + } /** * 尝试转换并加载内部类,例如java.lang.Thread.State =》java.lang.Thread$State diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java index 7b746bc6e..b9c3ab637 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java @@ -8,9 +8,9 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Filter; -import cn.hutool.core.lang.SimpleCache; import cn.hutool.core.lang.reflect.MethodHandleUtil; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.map.WeakConcurrentMap; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; @@ -36,15 +36,15 @@ public class ReflectUtil { /** * 构造对象缓存 */ - private static final SimpleCache, Constructor[]> CONSTRUCTORS_CACHE = new SimpleCache<>(); + private static final WeakConcurrentMap, Constructor[]> CONSTRUCTORS_CACHE = new WeakConcurrentMap<>(); /** * 字段缓存 */ - private static final SimpleCache, Field[]> FIELDS_CACHE = new SimpleCache<>(); + private static final WeakConcurrentMap, Field[]> FIELDS_CACHE = new WeakConcurrentMap<>(); /** * 方法缓存 */ - private static final SimpleCache, Method[]> METHODS_CACHE = new SimpleCache<>(); + private static final WeakConcurrentMap, Method[]> METHODS_CACHE = new WeakConcurrentMap<>(); // --------------------------------------------------------------------------------------------------------- Constructor @@ -86,7 +86,7 @@ public class ReflectUtil { @SuppressWarnings("unchecked") public static Constructor[] getConstructors(Class beanClass) throws SecurityException { Assert.notNull(beanClass); - return (Constructor[]) CONSTRUCTORS_CACHE.get(beanClass, () -> getConstructorsDirectly(beanClass)); + return (Constructor[]) CONSTRUCTORS_CACHE.computeIfAbsent(beanClass, () -> getConstructorsDirectly(beanClass)); } /** @@ -175,7 +175,7 @@ public class ReflectUtil { */ public static Field[] getFields(Class beanClass) throws SecurityException { Assert.notNull(beanClass); - return FIELDS_CACHE.get(beanClass, () -> getFieldsDirectly(beanClass, true)); + return FIELDS_CACHE.computeIfAbsent(beanClass, () -> getFieldsDirectly(beanClass, true)); } @@ -651,7 +651,7 @@ public class ReflectUtil { */ public static Method[] getMethods(Class beanClass) throws SecurityException { Assert.notNull(beanClass); - return METHODS_CACHE.get(beanClass, + return METHODS_CACHE.computeIfAbsent(beanClass, () -> getMethodsDirectly(beanClass, true, true)); } diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/SimpleCacheTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/SimpleCacheTest.java index e32c78a90..8b15d28e6 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/SimpleCacheTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/SimpleCacheTest.java @@ -50,7 +50,7 @@ public class SimpleCacheTest { final SimpleCache cache = new SimpleCache<>(); final ConcurrencyTester tester = new ConcurrencyTester(9000); tester.test(()-> cache.get("aaa", ()-> { - ThreadUtil.sleep(1000); + ThreadUtil.sleep(200); return "aaaValue"; })); diff --git a/hutool-core/src/test/java/cn/hutool/core/map/FuncMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/FuncMapTest.java new file mode 100755 index 000000000..1a26d127a --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/FuncMapTest.java @@ -0,0 +1,22 @@ +package cn.hutool.core.map; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; + +public class FuncMapTest { + + @Test + public void putGetTest(){ + final FuncMap map = new FuncMap<>(HashMap::new, + (key)->key.toString().toLowerCase(), + (value)->value.toString().toUpperCase()); + + map.put("aaa", "b"); + map.put("BBB", "c"); + + Assert.assertEquals("B", map.get("aaa")); + Assert.assertEquals("C", map.get("bbb")); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/map/WeakConcurrentMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/WeakConcurrentMapTest.java new file mode 100755 index 000000000..7824d1918 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/WeakConcurrentMapTest.java @@ -0,0 +1,52 @@ +package cn.hutool.core.map; + +import cn.hutool.core.thread.ConcurrencyTester; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import org.junit.Assert; +import org.junit.Test; + +public class WeakConcurrentMapTest { + + @Test + public void putAndGetTest(){ + final WeakConcurrentMap map = new WeakConcurrentMap<>(); + Object + key1 = new Object(), value1 = new Object(), + key2 = new Object(), value2 = new Object(), + key3 = new Object(), value3 = new Object(), + key4 = new Object(), value4 = new Object(); + map.put(key1, value1); + map.put(key2, value2); + map.put(key3, value3); + map.put(key4, value4); + + Assert.assertEquals(value1, map.get(key1)); + Assert.assertEquals(value2, map.get(key2)); + Assert.assertEquals(value3, map.get(key3)); + Assert.assertEquals(value4, map.get(key4)); + + // 清空引用 + //noinspection UnusedAssignment + key1 = null; + //noinspection UnusedAssignment + key2 = null; + + System.gc(); + ThreadUtil.sleep(200L); + + Assert.assertEquals(2, map.size()); + } + + @Test + public void getConcurrencyTest(){ + final WeakConcurrentMap cache = new WeakConcurrentMap<>(); + final ConcurrencyTester tester = new ConcurrencyTester(9000); + tester.test(()-> cache.computeIfAbsent("aaa" + RandomUtil.randomInt(2), (key)-> "aaaValue")); + + Assert.assertTrue(tester.getInterval() > 0); + String value = ObjectUtil.defaultIfNull(cache.get("aaa0"), cache.get("aaa1")); + Assert.assertEquals("aaaValue", value); + } +} diff --git a/hutool-extra/src/main/java/cn/hutool/extra/cglib/BeanCopierCache.java b/hutool-extra/src/main/java/cn/hutool/extra/cglib/BeanCopierCache.java index a2a012a10..e40712d1c 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/cglib/BeanCopierCache.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/cglib/BeanCopierCache.java @@ -1,6 +1,6 @@ package cn.hutool.extra.cglib; -import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.util.StrUtil; import net.sf.cglib.beans.BeanCopier; import net.sf.cglib.core.Converter; @@ -18,7 +18,7 @@ public enum BeanCopierCache { */ INSTANCE; - private final SimpleCache cache = new SimpleCache<>(); + private final WeakConcurrentMap cache = new WeakConcurrentMap<>(); /** * 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素 @@ -43,7 +43,7 @@ public enum BeanCopierCache { */ public BeanCopier get(Class srcClass, Class targetClass, boolean useConverter) { final String key = genKey(srcClass, targetClass, useConverter); - return cache.get(key, () -> BeanCopier.create(srcClass, targetClass, useConverter)); + return cache.computeIfAbsent(key, () -> BeanCopier.create(srcClass, targetClass, useConverter)); } /** diff --git a/hutool-script/src/main/java/cn/hutool/script/ScriptUtil.java b/hutool-script/src/main/java/cn/hutool/script/ScriptUtil.java index bc6965790..de674545c 100644 --- a/hutool-script/src/main/java/cn/hutool/script/ScriptUtil.java +++ b/hutool-script/src/main/java/cn/hutool/script/ScriptUtil.java @@ -1,6 +1,6 @@ package cn.hutool.script; -import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.util.StrUtil; import javax.script.Bindings; @@ -20,7 +20,7 @@ import javax.script.ScriptException; public class ScriptUtil { private static final ScriptEngineManager MANAGER = new ScriptEngineManager(); - private static final SimpleCache CACHE = new SimpleCache<>(); + private static final WeakConcurrentMap CACHE = new WeakConcurrentMap<>(); /** * 获得单例的{@link ScriptEngine} 实例 @@ -29,7 +29,7 @@ public class ScriptUtil { * @return {@link ScriptEngine} 实例 */ public static ScriptEngine getScript(String nameOrExtOrMime) { - return CACHE.get(nameOrExtOrMime, () -> createScript(nameOrExtOrMime)); + return CACHE.computeIfAbsent(nameOrExtOrMime, () -> createScript(nameOrExtOrMime)); } /**