add LockUtil and fix Cache

This commit is contained in:
Looly
2020-03-26 23:46:36 +08:00
parent 54760ea0a2
commit 0d7ef8f092
13 changed files with 379 additions and 218 deletions

View File

@@ -1,13 +1,11 @@
package cn.hutool.core.lang;
import cn.hutool.core.lang.func.Func0;
import java.io.Serializable;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import cn.hutool.core.lang.func.Func0;
import java.util.concurrent.locks.StampedLock;
/**
* 简单缓存,无超时实现,使用{@link WeakHashMap}实现缓存自动清理
@@ -21,10 +19,9 @@ public class SimpleCache<K, V> implements Serializable{
/** 池 */
private final Map<K, V> cache = new WeakHashMap<>();
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
private final ReadLock readLock = cacheLock.readLock();
private final WriteLock writeLock = cacheLock.writeLock();
// 乐观读写锁
private final StampedLock lock = new StampedLock ();
/**
* 从缓存池中查找值
@@ -33,15 +30,12 @@ public class SimpleCache<K, V> implements Serializable{
* @return 值
*/
public V get(K key) {
// 尝试读取缓存
readLock.lock();
V value;
long stamp = lock.readLock();
try {
value = cache.get(key);
return cache.get(key);
} finally {
readLock.unlock();
lock.unlockRead(stamp);
}
return value;
}
/**
@@ -52,12 +46,25 @@ public class SimpleCache<K, V> implements Serializable{
* @return 值对象
*/
public V get(K key, Func0<V> supplier) {
V v = get(key);
if (null == v && null != supplier) {
writeLock.lock();
try {
// 双重检查锁
if(null == supplier){
return 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;
v = cache.get(key);
// 双重检查,防止在竞争锁的过程中已经有其它线程写入
if(null == v) {
try {
v = supplier.call();
@@ -66,9 +73,9 @@ public class SimpleCache<K, V> implements Serializable{
}
cache.put(key, v);
}
} finally {
writeLock.unlock();
}
} finally {
lock.unlock(stamp);
}
return v;
}
@@ -80,11 +87,12 @@ public class SimpleCache<K, V> implements Serializable{
* @return 值
*/
public V put(K key, V value){
writeLock.lock();
// 独占写锁
final long stamp = lock.writeLock();
try {
cache.put(key, value);
} finally {
writeLock.unlock();
lock.unlockWrite(stamp);
}
return value;
}
@@ -96,11 +104,12 @@ public class SimpleCache<K, V> implements Serializable{
* @return 移除的值
*/
public V remove(K key) {
writeLock.lock();
// 独占写锁
final long stamp = lock.writeLock();
try {
return cache.remove(key);
} finally {
writeLock.unlock();
lock.unlockWrite(stamp);
}
}
@@ -108,11 +117,12 @@ public class SimpleCache<K, V> implements Serializable{
* 清空缓存池
*/
public void clear() {
writeLock.lock();
// 独占写锁
final long stamp = lock.writeLock();
try {
this.cache.clear();
} finally {
writeLock.unlock();
lock.unlockWrite(stamp);
}
}
}

View File

@@ -0,0 +1,43 @@
package cn.hutool.core.thread.lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock;
/**
* 锁相关工具
*
* @author looly
* @since 5.2.5
*/
public class LockUtil {
private static NoLock NO_LOCK = new NoLock();
/**
* 创建{@link StampedLock}锁
*
* @return {@link StampedLock}锁
*/
public static StampedLock createStampLock() {
return new StampedLock();
}
/**
* 创建{@link ReentrantReadWriteLock}锁
*
* @param fair 是否公平锁
* @return {@link ReentrantReadWriteLock}锁
*/
public static ReentrantReadWriteLock createReadWriteLock(boolean fair) {
return new ReentrantReadWriteLock(fair);
}
/**
* 获取单例的无锁对象
*
* @return {@link NoLock}
*/
public static NoLock getNoLock(){
return NO_LOCK;
}
}

View File

@@ -17,7 +17,7 @@ public class NoLock implements Lock{
}
@Override
public void lockInterruptibly() throws InterruptedException {
public void lockInterruptibly() {
}
@Override
@@ -25,8 +25,9 @@ public class NoLock implements Lock{
return true;
}
@SuppressWarnings("NullableProblems")
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
public boolean tryLock(long time, TimeUnit unit) {
return true;
}
@@ -34,6 +35,7 @@ public class NoLock implements Lock{
public void unlock() {
}
@SuppressWarnings("NullableProblems")
@Override
public Condition newCondition() {
return null;

View File

@@ -0,0 +1,46 @@
package cn.hutool.core.lang;
import cn.hutool.core.thread.ThreadUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class SimpleCacheTest {
@Before
public void putTest(){
final SimpleCache<String, String> cache = new SimpleCache<>();
ThreadUtil.execute(()->cache.put("key1", "value1"));
ThreadUtil.execute(()->cache.get("key1"));
ThreadUtil.execute(()->cache.put("key2", "value2"));
ThreadUtil.execute(()->cache.get("key2"));
ThreadUtil.execute(()->cache.put("key3", "value3"));
ThreadUtil.execute(()->cache.get("key3"));
ThreadUtil.execute(()->cache.put("key4", "value4"));
ThreadUtil.execute(()->cache.get("key4"));
ThreadUtil.execute(()->cache.get("key5", ()->"value5"));
cache.get("key5", ()->"value5");
}
@Test
public void getTest(){
final SimpleCache<String, String> cache = new SimpleCache<>();
cache.put("key1", "value1");
cache.get("key1");
cache.put("key2", "value2");
cache.get("key2");
cache.put("key3", "value3");
cache.get("key3");
cache.put("key4", "value4");
cache.get("key4");
cache.get("key5", ()->"value5");
Assert.assertEquals("value1", cache.get("key1"));
Assert.assertEquals("value2", cache.get("key2"));
Assert.assertEquals("value3", cache.get("key3"));
Assert.assertEquals("value4", cache.get("key4"));
Assert.assertEquals("value5", cache.get("key5"));
Assert.assertEquals("value6", cache.get("key6", ()-> "value6"));
}
}