mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
add LockUtil and fix Cache
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
@@ -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"));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user