Browse Source

fix dead lock bug

Looly 5 years ago
parent
commit
f7bf950073

+ 1 - 0
CHANGELOG.md

@@ -15,6 +15,7 @@
 * 【core   】     IterUtil添加List转Map的工具方法(pr#123@Gitee)
 
 ### Bug修复
+* 【core   】     修复SimpleCache死锁问题(issue#I1HOKB@Gitee)
 
 -------------------------------------------------------------------------------------------------------------
 

+ 18 - 29
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<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
 	 */
 	private final Map<K, V> cache;
 	// 乐观读写锁
-	private final StampedLock lock = new StampedLock();
+	private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 
 	/**
 	 * 构造,默认使用{@link WeakHashMap}实现缓存自动清理
@@ -53,11 +53,11 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, 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<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
 	 * @return 值对象
 	 */
 	public V get(K key, Func0<V> 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<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
 					}
 					cache.put(key, v);
 				}
+			} finally{
+				lock.writeLock().unlock();
 			}
-		} finally {
-			lock.unlock(stamp);
 		}
+
 		return v;
 	}
 
@@ -112,11 +101,11 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, 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<K, V> implements Iterable<Map.Entry<K, V>>, 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<K, V> implements Iterable<Map.Entry<K, V>>, 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<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
 	public Iterator<Map.Entry<K, V>> iterator() {
 		return this.cache.entrySet().iterator();
 	}
-}
+}

+ 15 - 0
hutool-core/src/main/java/cn/hutool/core/lang/func/Func.java

@@ -23,4 +23,19 @@ public interface Func<P, R> {
 	 */
 	@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);
+		}
+	}
 }

+ 14 - 0
hutool-core/src/main/java/cn/hutool/core/lang/func/Func0.java

@@ -20,4 +20,18 @@ public interface Func0<R> {
 	 * @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);
+		}
+	}
 }

+ 15 - 0
hutool-core/src/main/java/cn/hutool/core/lang/func/Func1.java

@@ -23,4 +23,19 @@ public interface Func1<P, R> {
 	 * @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);
+		}
+	}
 }

+ 14 - 0
hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc.java

@@ -22,4 +22,18 @@ public interface VoidFunc<P> {
 	 */
 	@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);
+		}
+	}
 }

+ 13 - 0
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);
+		}
+	}
 }

+ 14 - 0
hutool-core/src/main/java/cn/hutool/core/lang/func/VoidFunc1.java

@@ -20,4 +20,18 @@ public interface VoidFunc1<P> {
 	 * @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);
+		}
+	}
 }

+ 21 - 0
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);
+	}
 }