From a302a11483c9dca528ac596ccb8ce309a3585cd3 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 28 Nov 2020 23:29:10 +0800 Subject: [PATCH] add groupTimeInterval --- CHANGELOG.md | 2 + .../src/main/java/cn/hutool/cache/Cache.java | 12 +- .../java/cn/hutool/cache/CacheListener.java | 20 ++ .../cn/hutool/cache/impl/AbstractCache.java | 28 ++- .../java/cn/hutool/cache/test/CacheTest.java | 6 + .../hutool/core/date/GroupTimeInterval.java | 177 ++++++++++++++++++ .../cn/hutool/core/date/TimeInterval.java | 48 ++--- .../hutool/core/thread/ConcurrencyTester.java | 4 + .../cn/hutool/core/date/TimeIntervalTest.java | 20 ++ 9 files changed, 282 insertions(+), 35 deletions(-) create mode 100644 hutool-cache/src/main/java/cn/hutool/cache/CacheListener.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/date/GroupTimeInterval.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/date/TimeIntervalTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2586fe086..413de071b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ * 【core 】 ArrayUtil增加shuffle方法(pr#1255@Github) * 【core 】 ArrayUtil部分方法分离至PrimitiveArrayUtil * 【crypto 】 opt改为otp包(issue#1257@Github) +* 【cache 】 增加CacheListener(issue#1257@Github) +* 【core 】 TimeInterval支持分组(issue#1238@Github) ### Bug修复 * 【cron 】 修复CronTimer可能死循环的问题(issue#1224@Github) diff --git a/hutool-cache/src/main/java/cn/hutool/cache/Cache.java b/hutool-cache/src/main/java/cn/hutool/cache/Cache.java index 1478b192e..bc5172c71 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/Cache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/Cache.java @@ -45,7 +45,6 @@ public interface Cache extends Iterable, Serializable { * @param key 键 * @param object 缓存的对象 * @param timeout 失效时长,单位毫秒 - * @see Cache#put(Object, Object, long) */ void put(K key, V object, long timeout); @@ -159,4 +158,15 @@ public interface Cache extends Iterable, Serializable { * @return 是否包含key */ boolean containsKey(K key); + + /** + * 设置监听 + * + * @param listener 监听 + * @return this + * @since 5.5.2 + */ + default Cache setListener(CacheListener listener){ + return this; + } } diff --git a/hutool-cache/src/main/java/cn/hutool/cache/CacheListener.java b/hutool-cache/src/main/java/cn/hutool/cache/CacheListener.java new file mode 100644 index 000000000..7e5ab018b --- /dev/null +++ b/hutool-cache/src/main/java/cn/hutool/cache/CacheListener.java @@ -0,0 +1,20 @@ +package cn.hutool.cache; + +/** + * 缓存监听,用于实现缓存操作时的回调监听,例如缓存对象的移除事件等 + * + * @param 缓存键 + * @param 缓存值 + * @author looly + * @since 5.5.2 + */ +public interface CacheListener { + + /** + * 对象移除回调 + * + * @param key 键 + * @param cachedObject 被缓存的对象 + */ + void onRemove(K key, V cachedObject); +} diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java index 844fcd2ca..fa54d16ba 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java @@ -1,6 +1,7 @@ package cn.hutool.cache.impl; import cn.hutool.cache.Cache; +import cn.hutool.cache.CacheListener; import cn.hutool.core.collection.CopiedIter; import cn.hutool.core.lang.func.Func0; @@ -51,6 +52,11 @@ public abstract class AbstractCache implements Cache { */ protected AtomicLong missCount = new AtomicLong(); + /** + * 缓存监听 + */ + protected CacheListener listener; + // ---------------------------------------------------------------- put start @Override public void put(K key, V object) { @@ -164,7 +170,7 @@ public abstract class AbstractCache implements Cache { if (co.isExpired()) { missCount.getAndIncrement(); - } else{ + } else { // 命中 hitCount.getAndIncrement(); return co.get(isUpdateLastAccess); @@ -280,13 +286,29 @@ public abstract class AbstractCache implements Cache { // ---------------------------------------------------------------- common end /** - * 对象移除回调。默认无动作 + * 设置监听 + * + * @param listener 监听 + * @return this + * @since 5.5.2 + */ + public AbstractCache setListener(CacheListener listener) { + this.listener = listener; + return this; + } + + /** + * 对象移除回调。默认无动作
+ * 子类可重写此方法用于监听移除事件,如果重写,listener将无效 * * @param key 键 * @param cachedObject 被缓存的对象 */ protected void onRemove(K key, V cachedObject) { - // ignore + final CacheListener listener = this.listener; + if (null != listener) { + listener.onRemove(key, cachedObject); + } } /** diff --git a/hutool-cache/src/test/java/cn/hutool/cache/test/CacheTest.java b/hutool-cache/src/test/java/cn/hutool/cache/test/CacheTest.java index c1832ce28..1e843ae84 100644 --- a/hutool-cache/src/test/java/cn/hutool/cache/test/CacheTest.java +++ b/hutool-cache/src/test/java/cn/hutool/cache/test/CacheTest.java @@ -18,6 +18,12 @@ public class CacheTest { @Test public void fifoCacheTest(){ Cache fifoCache = CacheUtil.newFIFOCache(3); + fifoCache.setListener((key, value)->{ + // 监听测试,此测试中只有key1被移除,测试是否监听成功 + Assert.assertEquals("key1", key); + Assert.assertEquals("value1", value); + }); + fifoCache.put("key1", "value1", DateUnit.SECOND.getMillis() * 3); fifoCache.put("key2", "value2", DateUnit.SECOND.getMillis() * 3); fifoCache.put("key3", "value3", DateUnit.SECOND.getMillis() * 3); diff --git a/hutool-core/src/main/java/cn/hutool/core/date/GroupTimeInterval.java b/hutool-core/src/main/java/cn/hutool/core/date/GroupTimeInterval.java new file mode 100644 index 000000000..518959a8c --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/date/GroupTimeInterval.java @@ -0,0 +1,177 @@ +package cn.hutool.core.date; + +import cn.hutool.core.util.ObjectUtil; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 分组计时器
+ * 计算某几个过程花费的时间,精确到毫秒或纳秒 + * + * @author Looly + * @since 5.5.2 + */ +public class GroupTimeInterval implements Serializable { + private static final long serialVersionUID = 1L; + + private final boolean isNano; + protected final Map groupMap; + + /** + * 构造 + * + * @param isNano 是否使用纳秒计数,false则使用毫秒 + */ + public GroupTimeInterval(boolean isNano) { + this.isNano = isNano; + groupMap = new ConcurrentHashMap<>(); + } + + /** + * 清空所有定时记录 + * + * @return this + */ + public GroupTimeInterval clear(){ + this.groupMap.clear(); + return this; + } + + /** + * 开始计时并返回当前时间 + * + * @param id 分组ID + * @return 开始计时并返回当前时间 + */ + public long start(String id) { + final long time = getTime(); + this.groupMap.put(id, time); + return time; + } + + /** + * 重新计时并返回从开始到当前的持续时间秒
+ * 如果此分组下没有记录,则返回0; + * + * @param id 分组ID + * @return 重新计时并返回从开始到当前的持续时间 + */ + public long intervalRestart(String id) { + final long now = getTime(); + return now - ObjectUtil.defaultIfNull(this.groupMap.put(id, now), now); + } + + //----------------------------------------------------------- Interval + + /** + * 从开始到当前的间隔时间(毫秒数)
+ * 如果使用纳秒计时,返回纳秒差,否则返回毫秒差
+ * 如果分组下没有开始时间,返回{@code null} + * + * @param id 分组ID + * @return 从开始到当前的间隔时间(毫秒数) + */ + public long interval(String id) { + final Long lastTime = this.groupMap.get(id); + if (null == lastTime) { + return 0; + } + return getTime() - lastTime; + } + + /** + * 从开始到当前的间隔时间 + * + * @param id 分组ID + * @param dateUnit 时间单位 + * @return 从开始到当前的间隔时间(毫秒数) + */ + public long interval(String id, DateUnit dateUnit) { + final long intervalMs = isNano ? interval(id) / 1000000L : interval(id); + if (DateUnit.MS == dateUnit) { + return intervalMs; + } + return intervalMs / dateUnit.getMillis(); + } + + /** + * 从开始到当前的间隔时间(毫秒数) + * + * @param id 分组ID + * @return 从开始到当前的间隔时间(毫秒数) + */ + public long intervalMs(String id) { + return interval(id, DateUnit.MS); + } + + /** + * 从开始到当前的间隔秒数,取绝对值 + * + * @param id 分组ID + * @return 从开始到当前的间隔秒数,取绝对值 + */ + public long intervalSecond(String id) { + return interval(id, DateUnit.SECOND); + } + + /** + * 从开始到当前的间隔分钟数,取绝对值 + * + * @param id 分组ID + * @return 从开始到当前的间隔分钟数,取绝对值 + */ + public long intervalMinute(String id) { + return interval(id, DateUnit.MINUTE); + } + + /** + * 从开始到当前的间隔小时数,取绝对值 + * + * @param id 分组ID + * @return 从开始到当前的间隔小时数,取绝对值 + */ + public long intervalHour(String id) { + return interval(id, DateUnit.HOUR); + } + + /** + * 从开始到当前的间隔天数,取绝对值 + * + * @param id 分组ID + * @return 从开始到当前的间隔天数,取绝对值 + */ + public long intervalDay(String id) { + return interval(id, DateUnit.DAY); + } + + /** + * 从开始到当前的间隔周数,取绝对值 + * + * @param id 分组ID + * @return 从开始到当前的间隔周数,取绝对值 + */ + public long intervalWeek(String id) { + return interval(id, DateUnit.WEEK); + } + + /** + * 从开始到当前的间隔时间(毫秒数),返回XX天XX小时XX分XX秒XX毫秒 + * + * @param id 分组ID + * @return 从开始到当前的间隔时间(毫秒数) + */ + public String intervalPretty(String id) { + return DateUtil.formatBetween(intervalMs(id)); + } + + /** + * 获取时间的毫秒或纳秒数,纳秒非时间戳 + * + * @return 时间 + */ + private long getTime() { + return this.isNano ? System.nanoTime() : System.currentTimeMillis(); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TimeInterval.java b/hutool-core/src/main/java/cn/hutool/core/date/TimeInterval.java index 8168d2e70..2b58dbb9f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/TimeInterval.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/TimeInterval.java @@ -1,6 +1,6 @@ package cn.hutool.core.date; -import java.io.Serializable; +import cn.hutool.core.util.StrUtil; /** * 计时器
@@ -8,11 +8,9 @@ import java.io.Serializable; * * @author Looly */ -public class TimeInterval implements Serializable { +public class TimeInterval extends GroupTimeInterval { private static final long serialVersionUID = 1L; - - private long time; - private final boolean isNano; + private static final String DEFAULT_ID = StrUtil.EMPTY; /** * 构造,默认使用毫秒计数 @@ -23,10 +21,11 @@ public class TimeInterval implements Serializable { /** * 构造 + * * @param isNano 是否使用纳秒计数,false则使用毫秒 */ public TimeInterval(boolean isNano) { - this.isNano = isNano; + super(isNano); start(); } @@ -34,28 +33,25 @@ public class TimeInterval implements Serializable { * @return 开始计时并返回当前时间 */ public long start() { - time = getTime(isNano); - return time; + return start(DEFAULT_ID); } /** * @return 重新计时并返回从开始到当前的持续时间 */ public long intervalRestart() { - long now = getTime(isNano); - long d = now - time; - time = now; - return d; + return intervalRestart(DEFAULT_ID); } /** * 重新开始计算时间(重置开始时间) * * @return this + * @see #start() * @since 3.0.1 */ public TimeInterval restart() { - time = getTime(isNano); + start(DEFAULT_ID); return this; } @@ -68,7 +64,7 @@ public class TimeInterval implements Serializable { * @return 从开始到当前的间隔时间(毫秒数) */ public long interval() { - return getTime(isNano) - time; + return interval(DEFAULT_ID); } /** @@ -78,7 +74,7 @@ public class TimeInterval implements Serializable { * @since 4.6.7 */ public String intervalPretty() { - return DateUtil.formatBetween(intervalMs()); + return intervalPretty(DEFAULT_ID); } /** @@ -87,7 +83,7 @@ public class TimeInterval implements Serializable { * @return 从开始到当前的间隔时间(毫秒数) */ public long intervalMs() { - return isNano ? interval() / 1000000L : interval(); + return intervalMs(DEFAULT_ID); } /** @@ -96,7 +92,7 @@ public class TimeInterval implements Serializable { * @return 从开始到当前的间隔秒数,取绝对值 */ public long intervalSecond() { - return intervalMs() / DateUnit.SECOND.getMillis(); + return intervalSecond(DEFAULT_ID); } /** @@ -105,7 +101,7 @@ public class TimeInterval implements Serializable { * @return 从开始到当前的间隔分钟数,取绝对值 */ public long intervalMinute() { - return intervalMs() / DateUnit.MINUTE.getMillis(); + return intervalMinute(DEFAULT_ID); } /** @@ -114,7 +110,7 @@ public class TimeInterval implements Serializable { * @return 从开始到当前的间隔小时数,取绝对值 */ public long intervalHour() { - return intervalMs() / DateUnit.HOUR.getMillis(); + return intervalHour(DEFAULT_ID); } /** @@ -123,7 +119,7 @@ public class TimeInterval implements Serializable { * @return 从开始到当前的间隔天数,取绝对值 */ public long intervalDay() { - return intervalMs() / DateUnit.DAY.getMillis(); + return intervalDay(DEFAULT_ID); } /** @@ -132,16 +128,6 @@ public class TimeInterval implements Serializable { * @return 从开始到当前的间隔周数,取绝对值 */ public long intervalWeek() { - return intervalMs() / DateUnit.WEEK.getMillis(); - } - - /** - * 获取时间的毫秒或纳秒数,纳秒非时间戳 - * - * @param isNano 是否为高精度时间 - * @return 时间 - */ - private static long getTime(boolean isNano) { - return isNano ? System.nanoTime() : System.currentTimeMillis(); + return intervalWeek(DEFAULT_ID); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/thread/ConcurrencyTester.java b/hutool-core/src/main/java/cn/hutool/core/thread/ConcurrencyTester.java index 20b42f4ee..252294a70 100644 --- a/hutool-core/src/main/java/cn/hutool/core/thread/ConcurrencyTester.java +++ b/hutool-core/src/main/java/cn/hutool/core/thread/ConcurrencyTester.java @@ -21,6 +21,10 @@ public class ConcurrencyTester { private final TimeInterval timeInterval; private long interval; + /** + * 构造 + * @param threadSize 线程数 + */ public ConcurrencyTester(int threadSize) { this.sf = new SyncFinisher(threadSize); this.timeInterval = new TimeInterval(); diff --git a/hutool-core/src/test/java/cn/hutool/core/date/TimeIntervalTest.java b/hutool-core/src/test/java/cn/hutool/core/date/TimeIntervalTest.java new file mode 100644 index 000000000..67daa4d2f --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/date/TimeIntervalTest.java @@ -0,0 +1,20 @@ +package cn.hutool.core.date; + +import cn.hutool.core.lang.Console; +import cn.hutool.core.thread.ThreadUtil; +import org.junit.Test; + +public class TimeIntervalTest { + @Test + public void intervalGroupTest(){ + final TimeInterval timer = new TimeInterval(); + timer.start("1"); + ThreadUtil.sleep(800); + timer.start("2"); + ThreadUtil.sleep(900); + + + Console.log("Timer 1 took {} ms", timer.intervalMs("1")); + Console.log("Timer 2 took {} ms", timer.intervalMs("2")); + } +}