diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/StampedCache.java b/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/StampedCache.java index 83a67dc94..04a3b563c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/StampedCache.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/StampedCache.java @@ -111,7 +111,7 @@ public abstract class StampedCache extends AbstractCache { *
 	 *     1. 读取时无写入,不冲突,直接获取值
 	 *     2. 读取时无写入,但是乐观读时触发了并发异常,此时获取同步锁,获取新值
-	 *     4. 读取时有写入,此时获取同步锁,获取新值
+	 *     3. 读取时有写入,此时获取同步锁,获取新值
 	 * 
* * @param key 键 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/TimedCache.java b/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/TimedCache.java index 585c75362..80f30cefa 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/TimedCache.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/TimedCache.java @@ -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; /** * 定时缓存
- * 此缓存没有容量限制,对象只有在过期后才会被移除 + * 此缓存没有容量限制,对象只有在过期后才会被移除
+ * 此缓存采用读写乐观锁,用于可脏读的场景,不能使用LinkedHashMap * * @author Looly * @@ -57,7 +57,7 @@ public class TimedCache extends StampedCache { public TimedCache(final long timeout, final Map, CacheObj> map) { this.capacity = 0; this.timeout = timeout; - this.cacheMap = map; + this.cacheMap = Assert.isNotInstanceOf(LinkedHashMap.class, map); } // ---------------------------------------------------------------- prune diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/TimedReentrantCache.java b/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/TimedReentrantCache.java new file mode 100644 index 000000000..0f0112f3d --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/TimedReentrantCache.java @@ -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; + +/** + * 定时缓存
+ * 此缓存没有容量限制,对象只有在过期后才会被移除 + * + * @author Looly + * + * @param 键类型 + * @param 值类型 + */ +public class TimedReentrantCache extends ReentrantCache { + 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, CacheObj> map) { + this.capacity = 0; + this.timeout = timeout; + this.cacheMap = map; + } + + // ---------------------------------------------------------------- prune + /** + * 清理过期对象 + * + * @return 清理数 + */ + @Override + protected int pruneCache() { + int count = 0; + final Iterator> values = cacheObjIter(); + CacheObj 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 schedulePrune(final long delay) { + this.pruneJobFuture = GlobalPruneTimer.INSTANCE.schedule(this::prune, delay); + return this; + } + + /** + * 取消定时清理 + */ + public void cancelPruneSchedule() { + if (null != pruneJobFuture) { + pruneJobFuture.cancel(true); + } + } + +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/WeakCache.java b/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/WeakCache.java index d968b99cf..ecb6534dc 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/WeakCache.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/cache/impl/WeakCache.java @@ -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; + /** * 弱引用缓存
* 对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。
@@ -42,7 +44,7 @@ public class WeakCache extends TimedCache{ * @param timeout 超时时常,单位毫秒,-1或0表示无限制 */ public WeakCache(final long timeout) { - super(timeout, new WeakConcurrentMap<>()); + super(timeout, new WeakHashMap<>()); } @Override diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java index a2ad0551d..4cdd52d8f 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java @@ -1989,7 +1989,7 @@ public class CollUtil { * @param consumer {@link SerBiConsumer} 遍历的每条数据处理器 */ public static void forEach(final Iterator iterator, final SerBiConsumer consumer) { - IterUtil.forEach(iterator, consumer); + IterUtil.indexForEach(iterator, consumer); } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/collection/iter/IterUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/collection/iter/IterUtil.java index 4d28d30d6..f18b8c6ae 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/collection/iter/IterUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/collection/iter/IterUtil.java @@ -910,7 +910,7 @@ public class IterUtil { * @param iterator {@link Iterator} * @param consumer {@link SerBiConsumer} 遍历的每条数据处理器 */ - public static void forEach(final Iterator iterator, final SerBiConsumer consumer) { + public static void indexForEach(final Iterator iterator, final SerBiConsumer consumer) { if (iterator == null) { return; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateBetween.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateBetween.java index bf9883730..4219bbf39 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateBetween.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateBetween.java @@ -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; } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/Assert.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/Assert.java index 2dfc4d3c4..4c6b9851b 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/lang/Assert.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/Assert.java @@ -563,6 +563,7 @@ public class Assert { // endregion // region ----- notContain + /** * 断言给定字符串是否不被另一个字符串包含(即是否为子串),并使用指定的函数获取错误信息返回
* 如果非子串,返回子串,如果是子串,则抛出{@link IllegalArgumentException}异常。 @@ -689,7 +690,8 @@ public class Assert { // region ----- isInstanceOf and isAssignable /** - * 断言给定对象是否是给定类的实例 + * 断言给定对象是否是给定类的实例,如果不是则抛出异常
+ * 此方法用于限定对象的类型 *
 	 * Assert.instanceOf(Foo.class, foo);
 	 * 
@@ -706,7 +708,8 @@ public class Assert { } /** - * 断言给定对象是否是给定类的实例 + * 断言给定对象是否是给定类的实例
+ * 此方法用于限定对象的类型 *
 	 * Assert.instanceOf(Foo.class, foo, "foo must be an instance of class Foo");
 	 * 
@@ -728,6 +731,48 @@ public class Assert { return obj; } + /** + * 断言给定对象不是否是给定类的实例,如果是则抛出异常
+ * 此方法用于排除给定类型 + *
+	 * Assert.isNotInstanceOf(Foo.class, foo);
+	 * 
+ * + * @param 被检查对象泛型类型 + * @param type 被检查对象匹配的类型 + * @param obj 被检查对象 + * @return 被检查的对象 + * @throws IllegalArgumentException if the object is not an instance of clazz + * @see Class#isInstance(Object) + */ + public static T isNotInstanceOf(final Class type, final T obj) { + return isNotInstanceOf(type, obj, "Object [{}] must be not instanceof [{}]", obj, type); + } + + /** + * 断言给定对象是否不是给定类的实例,如果是则抛出异常
+ * 此方法用于排除给定类型 + *
+	 * Assert.isNotInstanceOf(Foo.class, foo, "foo must be not an Foo");
+	 * 
+ * + * @param 被检查对象泛型类型 + * @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 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}. *
@@ -816,6 +861,7 @@ public class Assert {
 	// endregion
 
 	// region ----- checkIndex
+
 	/**
 	 * 检查下标(数组、集合、字符串)是否符合要求,下标必须满足:
 	 *