From 4bc25e92d8dcbebef66c2f485e54d8b1fa02b666 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 31 Mar 2023 21:34:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DMapUtil.computeIfAbsent?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E5=AD=98=E5=9C=A8=E7=9A=84=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 + .../main/java/cn/hutool/core/map/MapUtil.java | 24 ++++-- .../java/cn/hutool/core/util/JdkUtil.java | 86 +++++++++++++++++++ .../cn/hutool/http/ssl/DefaultSSLInfo.java | 4 +- 4 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/util/JdkUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fe32fb9d..3b3c4b78d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,12 @@ * 【core 】 NumberUtil增加(pr#968@Gitee) * 【core 】 Number128增加hash和equals方法(pr#968@Gitee) * 【core 】 NamingCase.toCamelCase新增重载,可选是否转换其他字符为小写(issue#3031@ithub) +* 【core 】 新增JdkUtil ### 🐞Bug修复 * 【core 】 CollUtil.split优化切割列表参数判断,避免OOM(pr#3026@Github) * 【core 】 修复FileUtil.move传入相同目录或子目录丢失源目录的问题(pr#3032@Github) +* 【core 】 修复MapUtil.computeIfAbsent可能存在的并发问题(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 6a85ca71c..77425becd 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 @@ -9,6 +9,7 @@ import cn.hutool.core.lang.Pair; import cn.hutool.core.lang.TypeReference; import cn.hutool.core.stream.CollectorUtil; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.JdkUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; @@ -1459,30 +1460,35 @@ public class MapUtil { */ public static Map.Entry entry(K key, V value, boolean isImmutable) { return isImmutable ? - new AbstractMap.SimpleImmutableEntry<>(key, value) : - new AbstractMap.SimpleEntry<>(key, value); + new AbstractMap.SimpleImmutableEntry<>(key, value) : + new AbstractMap.SimpleEntry<>(key, value); } /** * 如果 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. + * This class should be removed once we drop Java 8 support.
+ * 参考:https://github.com/apache/dubbo/blob/3.2/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConcurrentHashMapUtils.java * * @param 键类型 * @param 值类型 * @param map Map * @param key 键 * @param mappingFunction 值不存在时值的生成函数 - * @see https://bugs.openjdk.java.net/browse/JDK-8161372 * @return 值 + * @see https://bugs.openjdk.java.net/browse/JDK-8161372 */ public static V computeIfAbsent(Map map, K key, Function mappingFunction) { - V value = map.get(key); - if (null == value) { - map.putIfAbsent(key, mappingFunction.apply(key)); - value = map.get(key); + if (JdkUtil.IS_JDK8) { + V value = map.get(key); + if (null == value) { + //map.putIfAbsent(key, mappingFunction.apply(key)); + value = map.computeIfAbsent(key, mappingFunction); + } + return value; + } else { + return map.computeIfAbsent(key, mappingFunction); } - return value; } } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/JdkUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/JdkUtil.java new file mode 100644 index 000000000..b7ad0ba65 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/util/JdkUtil.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package cn.hutool.core.util; + +/** + * JDK相关工具类,包括判断JDK版本等
+ * 工具部分方法来自fastjson2的JDKUtils + * + * @author fastjson, looly + */ +public class JdkUtil { + /** + * JDK版本 + */ + public static final int JVM_VERSION; + /** + * 是否JDK8
+ * 由于Hutool基于JDK8编译,当使用JDK版本低于8时,不支持。 + */ + public static final boolean IS_JDK8; + /** + * 是否大于等于JDK17 + */ + public static final boolean IS_AT_LEAST_JDK17; + + /** + * 是否Android环境 + */ + public static final boolean IS_ANDROID; + + static { + // JVM版本 + JVM_VERSION = _getJvmVersion(); + IS_JDK8 = 8 == JVM_VERSION; + IS_AT_LEAST_JDK17 = JVM_VERSION >= 17; + + // JVM名称 + final String jvmName = _getJvmName(); + IS_ANDROID = jvmName.equals("Dalvik"); + } + + /** + * 获取JVM名称 + * + * @return JVM名称 + */ + private static String _getJvmName() { + return System.getProperty("java.vm.name"); + } + + /** + * 根据{@code java.specification.version}属性值,获取版本号 + * + * @return 版本号 + */ + private static int _getJvmVersion() { + int jvmVersion = -1; + + try{ + String javaSpecVer = System.getProperty("java.specification.version"); + if (StrUtil.isNotBlank(javaSpecVer)) { + if (javaSpecVer.startsWith("1.")) { + javaSpecVer = javaSpecVer.substring(2); + } + if (javaSpecVer.indexOf('.') == -1) { + jvmVersion = Integer.parseInt(javaSpecVer); + } + } + } catch (Throwable ignore){ + // 默认JDK8 + jvmVersion = 8; + } + + return jvmVersion; + } +} diff --git a/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLInfo.java b/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLInfo.java index a5b7a9708..a84c6005e 100644 --- a/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLInfo.java +++ b/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLInfo.java @@ -1,6 +1,6 @@ package cn.hutool.http.ssl; -import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.JdkUtil; import javax.net.ssl.SSLSocketFactory; @@ -22,7 +22,7 @@ public class DefaultSSLInfo { static { TRUST_ANY_HOSTNAME_VERIFIER = new TrustAnyHostnameVerifier(); - if (StrUtil.equalsIgnoreCase("dalvik", System.getProperty("java.vm.name"))) { + if (JdkUtil.IS_ANDROID) { // 兼容android低版本SSL连接 DEFAULT_SSF = new AndroidSupportSSLFactory(); } else {