From 4fc72f3d5e36b8c934ab320381157f8e6bacfc72 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 6 Aug 2023 03:43:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20SafeConcurrentHashMap=20?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xyz/zhouxy/plusone/commons/base/JRE.java | 58 +++++++++++++++++++ .../collection/SafeConcurrentHashMap.java | 29 ++++++++-- 2 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java b/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java new file mode 100644 index 0000000..a4ae6cf --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.zhouxy.plusone.commons.base; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.reflect.MethodUtils; + +/** + * JRE version + */ +public class JRE { + + private static final int JAVA_8 = 8; + + public static final int CURRENT_VERSION = getJre(); + + public static boolean isJava8() { + return CURRENT_VERSION == JAVA_8; + } + + private static int getJre() { + String version = System.getProperty("java.version"); + boolean isBlank = StringUtils.isBlank(version); + if (!isBlank && version.startsWith("1.8")) { + return JAVA_8; + } + // if JRE version is 9 or above, we can get version from + // java.lang.Runtime.version() + try { + Object javaRunTimeVersion = MethodUtils.invokeMethod(Runtime.getRuntime(), "version"); + return (int) MethodUtils.invokeMethod(javaRunTimeVersion, "major"); + } catch (Exception e) { + // Can't determine current JRE version (maybe java.version is null), + // assuming that JRE version is 8. + } + // default java 8 + return JAVA_8; + } + + private JRE() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java index f76e916..97e01bb 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java @@ -1,11 +1,14 @@ package xyz.zhouxy.plusone.commons.collection; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import javax.annotation.concurrent.ThreadSafe; +import xyz.zhouxy.plusone.commons.base.JRE; + @ThreadSafe public class SafeConcurrentHashMap extends ConcurrentHashMap { @@ -83,11 +86,27 @@ public class SafeConcurrentHashMap extends ConcurrentHashMap { /** {@inheritDoc} */ @Override public V computeIfAbsent(K key, Function mappingFunction) { - V v = get(key); - if (null == v) { - putIfAbsent(key, mappingFunction.apply(key)); - v = get(key); + Objects.requireNonNull(mappingFunction); + if (JRE.isJava8()) { + V v = get(key); + if (null == v) { + // this bug fix methods maybe cause `mappingFunction.apply` multiple calls. + v = mappingFunction.apply(key); + if (null == v) { + return null; + } + final V res = putIfAbsent(key, v); + if (null != res) { + // if pre value present, means other thread put value already, + // and putIfAbsent not effect + // return exist value + return res; + } + // if pre value is null, means putIfAbsent effected, return current value + } + return v; + } else { + return computeIfAbsent(key, mappingFunction); } - return v; } }