From 84e8456377127e301fc17887462a969370be2b3b Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 2 Apr 2023 21:37:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DSafeConcurrentHashMap.compute?= =?UTF-8?q?IfAbsent=E5=8F=AF=E8=83=BD=E5=AD=98=E5=9C=A8=E7=9A=84=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E4=B8=BAnull=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +- .../main/java/cn/hutool/core/map/MapUtil.java | 47 +++++++++++++++---- .../core/map/SafeConcurrentHashMap.java | 10 +--- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a06cd3856..c6613efe5 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.8.17.M1 (2023-04-01) +# 5.8.17.M1 (2023-04-02) ### 🐣新特性 * 【core 】 SerializeUtil.deserialize增加白名单类,避免RCE vulnerability(issue#3021@Github) @@ -17,6 +17,7 @@ ### 🐞Bug修复 * 【core 】 CollUtil.split优化切割列表参数判断,避免OOM(pr#3026@Github) * 【core 】 修复FileUtil.move传入相同目录或子目录丢失源目录的问题(pr#3032@Github) +* 【core 】 修复SafeConcurrentHashMap.computeIfAbsent可能存在的结果为null的情况(issue#I6RVMY@Gitee) ------------------------------------------------------------------------------------------------------------- # 5.8.16 (2023-03-26) 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 6d9ca97f4..2a7c79d08 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 @@ -1481,17 +1481,46 @@ public class MapUtil { */ public static V computeIfAbsent(Map map, K key, Function mappingFunction) { if (JdkUtil.IS_JDK8) { - V value = map.get(key); - if (null == value) { - map.putIfAbsent(key, mappingFunction.apply(key)); - value = map.get(key); - - // 判空后调用依旧无法解决死循环问题 - //value = map.computeIfAbsent(key, mappingFunction); - } - return value; + return computeIfAbsentForJdk8(map, key, mappingFunction); } else { return map.computeIfAbsent(key, mappingFunction); } } + + /** + * 如果 key 对应的 value 不存在,则使用获取 mappingFunction 重新计算后的值,并保存为该 key 的 value,否则返回 value。
+ * 解决使用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. + * + *

+ * 注意此方法只能用于JDK8 + *

+ * + * @param 键类型 + * @param 值类型 + * @param map Map,一般用于线程安全的Map + * @param key 键 + * @param mappingFunction 值计算函数 + * @return 值 + * @see https://bugs.openjdk.java.net/browse/JDK-8161372 + */ + public static V computeIfAbsentForJdk8(final Map map, final K key, final Function mappingFunction) { + V value = map.get(key); + if (null == value) { + value = mappingFunction.apply(key); + final V res = map.putIfAbsent(key, value); + if(null != res){ + // issues#I6RVMY + // 如果旧值存在,说明其他线程已经赋值成功,putIfAbsent没有执行,返回旧值 + return res; + } + // 如果旧值不存在,说明赋值成功,返回当前值 + + // Dubbo的解决方式,判空后调用依旧无法解决死循环问题 + // 见:Issue2349Test + //value = map.computeIfAbsent(key, mappingFunction); + } + return value; + } } 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 index 4c043d117..ea0ef7758 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/SafeConcurrentHashMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/SafeConcurrentHashMap.java @@ -72,15 +72,7 @@ public class SafeConcurrentHashMap extends ConcurrentHashMap { @Override public V computeIfAbsent(K key, Function mappingFunction) { if (JdkUtil.IS_JDK8) { - V value = get(key); - if (null == value) { - putIfAbsent(key, mappingFunction.apply(key)); - value = get(key); - - // 判空后调用依旧无法解决死循环问题 - //value = map.computeIfAbsent(key, mappingFunction); - } - return value; + return MapUtil.computeIfAbsentForJdk8(this, key, mappingFunction); } else { return super.computeIfAbsent(key, mappingFunction); }