add TimedReentrantCache

This commit is contained in:
Looly 2024-12-21 11:51:59 +08:00
parent 7d1438b665
commit 35505dfbf8
8 changed files with 166 additions and 14 deletions

View File

@ -111,7 +111,7 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
* <pre>
* 1. 读取时无写入不冲突直接获取值
* 2. 读取时无写入但是乐观读时触发了并发异常此时获取同步锁获取新值
* 4. 读取时有写入此时获取同步锁获取新值
* 3. 读取时有写入此时获取同步锁获取新值
* </pre>
*
* @param key

View File

@ -17,16 +17,16 @@
package org.dromara.hutool.core.cache.impl;
import org.dromara.hutool.core.cache.GlobalPruneTimer;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.mutable.Mutable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
/**
* 定时缓存<br>
* 此缓存没有容量限制对象只有在过期后才会被移除
* 此缓存没有容量限制对象只有在过期后才会被移除<br>
* 此缓存采用读写乐观锁用于可脏读的场景不能使用LinkedHashMap
*
* @author Looly
*
@ -57,7 +57,7 @@ public class TimedCache<K, V> extends StampedCache<K, V> {
public TimedCache(final long timeout, final Map<Mutable<K>, CacheObj<K, V>> map) {
this.capacity = 0;
this.timeout = timeout;
this.cacheMap = map;
this.cacheMap = Assert.isNotInstanceOf(LinkedHashMap.class, map);
}
// ---------------------------------------------------------------- prune

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed 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 org.dromara.hutool.core.cache.impl;
import org.dromara.hutool.core.cache.GlobalPruneTimer;
import org.dromara.hutool.core.lang.mutable.Mutable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
/**
* 定时缓存<br>
* 此缓存没有容量限制对象只有在过期后才会被移除
*
* @author Looly
*
* @param <K> 键类型
* @param <V> 值类型
*/
public class TimedReentrantCache<K, V> extends ReentrantCache<K, V> {
private static final long serialVersionUID = 1L;
/** 正在执行的定时任务 */
private ScheduledFuture<?> pruneJobFuture;
/**
* 构造
*
* @param timeout 超时过期时长单位毫秒
*/
public TimedReentrantCache(final long timeout) {
this(timeout, new HashMap<>());
}
/**
* 构造
*
* @param timeout 过期时长
* @param map 存储缓存对象的map
*/
public TimedReentrantCache(final long timeout, final Map<Mutable<K>, CacheObj<K, V>> map) {
this.capacity = 0;
this.timeout = timeout;
this.cacheMap = map;
}
// ---------------------------------------------------------------- prune
/**
* 清理过期对象
*
* @return 清理数
*/
@Override
protected int pruneCache() {
int count = 0;
final Iterator<CacheObj<K, V>> values = cacheObjIter();
CacheObj<K, V> co;
while (values.hasNext()) {
co = values.next();
if (co.isExpired()) {
values.remove();
onRemove(co.key, co.obj);
count++;
}
}
return count;
}
// ---------------------------------------------------------------- auto prune
/**
* 定时清理
*
* @param delay 间隔时长单位毫秒
* @return this
*/
public TimedReentrantCache<K, V> schedulePrune(final long delay) {
this.pruneJobFuture = GlobalPruneTimer.INSTANCE.schedule(this::prune, delay);
return this;
}
/**
* 取消定时清理
*/
public void cancelPruneSchedule() {
if (null != pruneJobFuture) {
pruneJobFuture.cancel(true);
}
}
}

View File

@ -22,6 +22,8 @@ import org.dromara.hutool.core.lang.mutable.Mutable;
import org.dromara.hutool.core.lang.ref.Ref;
import org.dromara.hutool.core.map.reference.WeakConcurrentMap;
import java.util.WeakHashMap;
/**
* 弱引用缓存<br>
* 对于一个给定的键其映射的存在并不阻止垃圾回收器对该键的丢弃这就使该键成为可终止的被终止然后被回收<br>
@ -42,7 +44,7 @@ public class WeakCache<K, V> extends TimedCache<K, V>{
* @param timeout 超时时常单位毫秒-1或0表示无限制
*/
public WeakCache(final long timeout) {
super(timeout, new WeakConcurrentMap<>());
super(timeout, new WeakHashMap<>());
}
@Override

View File

@ -1989,7 +1989,7 @@ public class CollUtil {
* @param consumer {@link SerBiConsumer} 遍历的每条数据处理器
*/
public static <T> void forEach(final Iterator<T> iterator, final SerBiConsumer<Integer, T> consumer) {
IterUtil.forEach(iterator, consumer);
IterUtil.indexForEach(iterator, consumer);
}
/**

View File

@ -910,7 +910,7 @@ public class IterUtil {
* @param iterator {@link Iterator}
* @param consumer {@link SerBiConsumer} 遍历的每条数据处理器
*/
public static <T> void forEach(final Iterator<T> iterator, final SerBiConsumer<Integer, T> consumer) {
public static <T> void indexForEach(final Iterator<T> iterator, final SerBiConsumer<Integer, T> consumer) {
if (iterator == null) {
return;
}

View File

@ -173,9 +173,7 @@ public class DateBetween implements Serializable {
endCal.set(Calendar.YEAR, beginCal.get(Calendar.YEAR));
final long between = endCal.getTimeInMillis() - beginCal.getTimeInMillis();
if (between < 0) {
return result - 1;
}
return between < 0 ? result - 1 : result;
}
/**

View File

@ -563,6 +563,7 @@ public class Assert {
// endregion
// region ----- notContain
/**
* 断言给定字符串是否不被另一个字符串包含即是否为子串并使用指定的函数获取错误信息返回<br>
* 如果非子串返回子串如果是子串则抛出{@link IllegalArgumentException}异常
@ -689,7 +690,8 @@ public class Assert {
// region ----- isInstanceOf and isAssignable
/**
* 断言给定对象是否是给定类的实例
* 断言给定对象是否是给定类的实例如果不是则抛出异常<br>
* 此方法用于限定对象的类型
* <pre class="code">
* Assert.instanceOf(Foo.class, foo);
* </pre>
@ -706,7 +708,8 @@ public class Assert {
}
/**
* 断言给定对象是否是给定类的实例
* 断言给定对象是否是给定类的实例<br>
* 此方法用于限定对象的类型
* <pre class="code">
* Assert.instanceOf(Foo.class, foo, "foo must be an instance of class Foo");
* </pre>
@ -728,6 +731,48 @@ public class Assert {
return obj;
}
/**
* 断言给定对象不是否是给定类的实例如果是则抛出异常<br>
* 此方法用于排除给定类型
* <pre class="code">
* Assert.isNotInstanceOf(Foo.class, foo);
* </pre>
*
* @param <T> 被检查对象泛型类型
* @param type 被检查对象匹配的类型
* @param obj 被检查对象
* @return 被检查的对象
* @throws IllegalArgumentException if the object is not an instance of clazz
* @see Class#isInstance(Object)
*/
public static <T> T isNotInstanceOf(final Class<?> type, final T obj) {
return isNotInstanceOf(type, obj, "Object [{}] must be not instanceof [{}]", obj, type);
}
/**
* 断言给定对象是否不是给定类的实例如果是则抛出异常<br>
* 此方法用于排除给定类型
* <pre class="code">
* Assert.isNotInstanceOf(Foo.class, foo, "foo must be not an Foo");
* </pre>
*
* @param <T> 被检查对象泛型类型
* @param type 被检查对象匹配的类型
* @param obj 被检查对象
* @param errorMsgTemplate 异常时的消息模板
* @param params 参数列表
* @return 被检查对象
* @throws IllegalArgumentException if the object is an instance of clazz
* @see Class#isInstance(Object)
*/
public static <T> T isNotInstanceOf(final Class<?> type, final T obj, final String errorMsgTemplate, final Object... params) throws IllegalArgumentException {
notNull(type, "Type to check against must not be null");
if (type.isInstance(obj)) {
throw new IllegalArgumentException(StrUtil.format(errorMsgTemplate, params));
}
return obj;
}
/**
* 断言 {@code superType.isAssignableFrom(subType)} 是否为 {@code true}.
* <pre class="code">
@ -816,6 +861,7 @@ public class Assert {
// endregion
// region ----- checkIndex
/**
* 检查下标数组集合字符串是否符合要求下标必须满足
*