From f7bf950073e43fa1d0415702c8677292ab5d038f Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 19 May 2020 18:31:30 +0800 Subject: [PATCH] fix dead lock bug --- CHANGELOG.md | 1 + .../java/cn/hutool/core/lang/SimpleCache.java | 47 +++++++------------ .../java/cn/hutool/core/lang/func/Func.java | 15 ++++++ .../java/cn/hutool/core/lang/func/Func0.java | 14 ++++++ .../java/cn/hutool/core/lang/func/Func1.java | 15 ++++++ .../cn/hutool/core/lang/func/VoidFunc.java | 14 ++++++ .../cn/hutool/core/lang/func/VoidFunc0.java | 13 +++++ .../cn/hutool/core/lang/func/VoidFunc1.java | 14 ++++++ .../cn/hutool/core/lang/SingletonTest.java | 21 +++++++++ 9 files changed, 125 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6d9cb00b..73b88515c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * 【core 】 IterUtil添加List转Map的工具方法(pr#123@Gitee) ### Bug修复 +* 【core 】 修复SimpleCache死锁问题(issue#I1HOKB@Gitee) ------------------------------------------------------------------------------------------------------------- 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 1f4ea3a75..c8149f759 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 @@ -6,7 +6,7 @@ import java.io.Serializable; import java.util.Iterator; import java.util.Map; import java.util.WeakHashMap; -import java.util.concurrent.locks.StampedLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 简单缓存,无超时实现,默认使用{@link WeakHashMap}实现缓存自动清理 @@ -23,7 +23,7 @@ public class SimpleCache implements Iterable>, Serializabl */ private final Map cache; // 乐观读写锁 - private final StampedLock lock = new StampedLock(); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); /** * 构造,默认使用{@link WeakHashMap}实现缓存自动清理 @@ -53,11 +53,11 @@ public class SimpleCache implements Iterable>, Serializabl * @return 值 */ public V get(K key) { - long stamp = lock.readLock(); + lock.readLock().lock(); try { return cache.get(key); } finally { - lock.unlockRead(stamp); + lock.readLock().unlock(); } } @@ -69,23 +69,11 @@ public class SimpleCache implements Iterable>, Serializabl * @return 值对象 */ public V get(K key, Func0 supplier) { - if (null == supplier) { - return get(key); - } + V v = get(key); - long stamp = lock.readLock(); - V v; - try { - v = cache.get(key); - if (null == v) { - // 尝试转换独占写锁 - long writeStamp = lock.tryConvertToWriteLock(stamp); - if (0 == writeStamp) { - // 转换失败,手动更新为写锁 - lock.unlockRead(stamp); - writeStamp = lock.writeLock(); - } - stamp = writeStamp; + if(null == v && null != supplier){ + lock.writeLock().lock(); + try{ v = cache.get(key); // 双重检查,防止在竞争锁的过程中已经有其它线程写入 if (null == v) { @@ -96,10 +84,11 @@ public class SimpleCache implements Iterable>, Serializabl } cache.put(key, v); } + } finally{ + lock.writeLock().unlock(); } - } finally { - lock.unlock(stamp); } + return v; } @@ -112,11 +101,11 @@ public class SimpleCache implements Iterable>, Serializabl */ public V put(K key, V value) { // 独占写锁 - final long stamp = lock.writeLock(); + lock.writeLock().lock(); try { cache.put(key, value); } finally { - lock.unlockWrite(stamp); + lock.writeLock().unlock(); } return value; } @@ -129,11 +118,11 @@ public class SimpleCache implements Iterable>, Serializabl */ public V remove(K key) { // 独占写锁 - final long stamp = lock.writeLock(); + lock.writeLock().lock(); try { return cache.remove(key); } finally { - lock.unlockWrite(stamp); + lock.writeLock().unlock(); } } @@ -142,11 +131,11 @@ public class SimpleCache implements Iterable>, Serializabl */ public void clear() { // 独占写锁 - final long stamp = lock.writeLock(); + lock.writeLock().lock(); try { this.cache.clear(); } finally { - lock.unlockWrite(stamp); + lock.writeLock().unlock(); } } @@ -154,4 +143,4 @@ public class SimpleCache implements Iterable>, Serializabl public Iterator> iterator() { return this.cache.entrySet().iterator(); } -} +} \ No newline at end of file diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/Func.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/Func.java index 8a91da6f1..a22f28ba6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/Func.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/Func.java @@ -23,4 +23,19 @@ public interface Func { */ @SuppressWarnings("unchecked") R call(P... parameters) throws Exception; + + /** + * 执行函数,异常包装为RuntimeException + * + * @param parameters 参数列表 + * @return 函数执行结果 + */ + @SuppressWarnings("unchecked") + default R callWithRuntimeException(P... parameters){ + try { + return call(parameters); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/Func0.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/Func0.java index c0cbe71d8..bdc8db679 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/Func0.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/Func0.java @@ -20,4 +20,18 @@ public interface Func0 { * @throws Exception 自定义异常 */ R call() throws Exception; + + /** + * 执行函数,异常包装为RuntimeException + * + * @return 函数执行结果 + * @since 5.3.6 + */ + default R callWithRuntimeException(){ + try { + return call(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/Func1.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/Func1.java index 3306e82fd..1967f08d1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/Func1.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/Func1.java @@ -23,4 +23,19 @@ public interface Func1 { * @throws Exception 自定义异常 */ R call(P parameter) throws Exception; + + /** + * 执行函数,异常包装为RuntimeException + * + * @param parameter 参数 + * @return 函数执行结果 + * @since 5.3.6 + */ + default R callWithRuntimeException(P parameter){ + try { + return call(parameter); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc.java index 4034b79ff..931599f20 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc.java @@ -22,4 +22,18 @@ public interface VoidFunc

{ */ @SuppressWarnings("unchecked") void call(P... parameters) throws Exception; + + /** + * 执行函数,异常包装为RuntimeException + * + * @param parameters 参数列表 + */ + @SuppressWarnings("unchecked") + default void callWithRuntimeException(P... parameters){ + try { + call(parameters); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc0.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc0.java index fd80af43b..a2a8ee54e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc0.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc0.java @@ -19,4 +19,17 @@ public interface VoidFunc0 { * @throws Exception 自定义异常 */ void call() throws Exception; + + /** + * 执行函数,异常包装为RuntimeException + * + * @since 5.3.6 + */ + default void callWithRuntimeException(){ + try { + call(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc1.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc1.java index ac32f634b..4cade382b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc1.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc1.java @@ -20,4 +20,18 @@ public interface VoidFunc1

{ * @throws Exception 自定义异常 */ void call(P parameter) throws Exception; + + /** + * 执行函数,异常包装为RuntimeException + * + * @param parameter 参数 + * @since 5.3.6 + */ + default void callWithRuntimeException(P parameter){ + try { + call(parameter); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/SingletonTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/SingletonTest.java index 52c2f564b..1a9d0a434 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/SingletonTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/SingletonTest.java @@ -3,6 +3,7 @@ package cn.hutool.core.lang; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.thread.ThreadUtil; import lombok.Data; +import org.junit.Assert; import org.junit.Test; public class SingletonTest { @@ -27,4 +28,24 @@ public class SingletonTest { private String name; private String age; } + + /** + * 测试单例构建属性锁死问题 + * C构建单例时候,同时构建B,此时在SimpleCache中会有写锁竞争(写入C时获取了写锁,此时要写入B,也要获取写锁) + */ + @Test(timeout = 1000L) + public void reentrantTest(){ + final C c = Singleton.get(C.class); + Assert.assertEquals("aaa", c.getB().getA()); + } + + @Data + static class B{ + private String a = "aaa"; + } + + @Data + static class C{ + private B b = Singleton.get(B.class); + } }