22import org .jetbrains .annotations .NotNull ;
33import org .jetbrains .annotations .Nullable ;
44
5+ import java .util .Iterator ;
56import java .util .LinkedHashMap ;
67import java .util .Map ;
78import java .util .Timer ;
89import java .util .TimerTask ;
10+ import java .util .concurrent .ExecutorService ;
11+ import java .util .concurrent .Executors ;
12+ import java .util .function .Consumer ;
913
1014import fybug .nulll .pdconcurrent .ObjLock ;
1115import fybug .nulll .pdconcurrent .SyLock ;
1216import lombok .AllArgsConstructor ;
1317import lombok .Getter ;
18+ import lombok .NonNull ;
1419import lombok .Setter ;
1520import lombok .experimental .Accessors ;
21+ import lombok .val ;
1622
1723/**
1824 * <h2>基于时间控制的缓存.</h2>
1925 * <p>
2026 * 内部使用 {@link LinkedHashMap} 实现的缓存工具,可指定数据过期的时间(毫秒)。<br/>
21- * 数据不会因为当前被持有而不会过期,因为数据基于时间控制,内部不使用 {@link java.lang.ref.Reference} 维护。
27+ * 数据不会因为当前被持有而不会过期,因为数据基于时间控制,内部不使用 {@link java.lang.ref.Reference} 维护。<br/>
28+ * 数据过期或因为任何原因被移除都会触发回收事件,在 {@link #putData(Object, Object, long, Consumer)} 最后一个参数指定,接口传入当前数据。<br/>
29+ * 回收事件执行时在一个线程池内执行,可使用 {@link #setGcThread(ExecutorService)} 指定,默认为 {@link Executors#newCachedThreadPool()}
30+ * <br/><br/>
31+ * <b>注意:{@link #clear()} 不会触发回收事件,因为这是缓存清空操作而不是单独回收数据。</b>
2232 * <br/><br/>
2333 * 内部会使用线程检查当前在热度排序中最后的几个数据,过期则会被主动移除。<br/>
2434 * 可通过 {@link #closeTimeTask()} 关闭扫描线程,效果等同于构造时传入的扫描间隔为 0,在不需要该工具时也可以使用 {@link #closeTimeTaskAndClear()} 清空数据并关闭扫描线程。<br/>
3444 * </ul>
3545 *
3646 * @author fybug
37- * @version 0.0.1
47+ * @version 0.0.2
3848 * @since memory 0.0.2
3949 */
4050public
@@ -52,6 +62,16 @@ class TimeMapCache<K, V> {
5262 /** 从尾部扫描的数量 */
5363 @ Setter protected volatile int scarrenNum ;
5464
65+ /**
66+ * 回收事件执行线程池
67+ *
68+ * @since 0.0.2
69+ */
70+ @ NonNull
71+ @ Setter
72+ @ Getter
73+ protected ExecutorService GcThread = Executors .newCachedThreadPool ();
74+
5575 //----------------------------------------------------------------------------------------------
5676
5777 /** 构造缓存,使用默认参数 */
@@ -91,7 +111,7 @@ class TimeMapCache<K, V> {
91111 * 缓存检查任务
92112 *
93113 * @author fybug
94- * @version 0.0.1
114+ * @version 0.0.2
95115 * @since TimeMapCache 0.0.1
96116 */
97117 protected final
@@ -102,14 +122,10 @@ void run() {
102122 LOCK .write (() -> {
103123 var now = System .currentTimeMillis ();
104124 var i = 0 ;
105-
106125 var iter = map .entrySet ().iterator ();
107- Map .Entry <K , Enty <V >> v ;
108- while ( iter .hasNext () && i ++ < scarrenNum ){
109- v = iter .next ();
110- if (v .getValue ().maxtime <= now )
111- iter .remove ();
112- }
126+ // 检查
127+ while ( iter .hasNext () && i ++ < scarrenNum )
128+ CheckVal (iter , now );
113129 });
114130 }
115131 }
@@ -136,7 +152,21 @@ void run() {
136152 */
137153 public
138154 void putData (@ NotNull K k , @ Nullable V v , long datatime )
139- { LOCK .write (() -> map .put (k , new Enty <>(System .currentTimeMillis () + datatime , v ))); }
155+ { putData (k , v , datatime , null ); }
156+
157+ /**
158+ * 放入数据
159+ *
160+ * @param k 数据的键
161+ * @param v 数据内容
162+ * @param datatime 数据的过期时间,相对于当前
163+ * @param endrun 数据被回收事件回调,接口传入当前数据,为空则不触发
164+ *
165+ * @since 0.0.2
166+ */
167+ public
168+ void putData (@ NotNull K k , @ Nullable V v , long datatime , @ Nullable Consumer <V > endrun )
169+ { LOCK .write (() -> map .put (k , new Enty <>(System .currentTimeMillis () + datatime , v , endrun ))); }
140170
141171 //-------------------------------------
142172
@@ -146,9 +176,13 @@ void putData(@NotNull K k, @Nullable V v, long datatime)
146176 * @param k 数据的键
147177 */
148178 public
149- void removeData (@ NotNull K k ) { LOCK .write (() -> map .remove (k )); }
179+ void removeData (@ NotNull K k ) { LOCK .write (() -> GcVal ( map .remove (k ) )); }
150180
151- /** 清除所有数据 */
181+ /**
182+ * 清除所有数据
183+ * <p>
184+ * <b>不会触发回收事件</b>
185+ */
152186 public
153187 void clear () { LOCK .write (map ::clear ); }
154188
@@ -189,7 +223,7 @@ V getData(@NotNull K k, long newDatatime) {
189223 map .put (k , v );
190224 return v .val ;
191225 }
192- map .remove (k );
226+ GcVal ( map .remove (k ) );
193227 return null ;
194228 });
195229 }
@@ -207,7 +241,7 @@ V getData(@NotNull K k, long newDatatime) {
207241 /** 检查数据是否可用 */
208242 protected
209243 boolean check (@ NotNull K k )
210- { return map .getOrDefault (k , new Enty <>(0 , null )).maxtime > System .currentTimeMillis (); }
244+ { return map .getOrDefault (k , new Enty <>(0 , null , null )).maxtime > System .currentTimeMillis (); }
211245
212246 //-------------------------------------
213247
@@ -220,8 +254,8 @@ boolean check(@NotNull K k)
220254 */
221255 public
222256 long dataHasTime (@ NotNull K k ) {
223- return LOCK .read (() -> Math .max (
224- map . getOrDefault ( k , new Enty <>( 0 , null )). maxtime - System .currentTimeMillis (), 0 ));
257+ return LOCK .read (() -> Math .max (map . getOrDefault ( k , new Enty <>( 0 , null , null )). maxtime -
258+ System .currentTimeMillis (), 0 ));
225259 }
226260
227261 /**
@@ -234,12 +268,9 @@ void trimData() {
234268 LOCK .write (() -> {
235269 long nowtime = System .currentTimeMillis ();
236270 var iter = map .entrySet ().iterator ();
237- Map .Entry <K , Enty <V >> v ;
238- while ( iter .hasNext () ){
239- v = iter .next ();
240- if (v .getValue ().maxtime <= nowtime )
241- iter .remove ();
242- }
271+ // 检查
272+ while ( iter .hasNext () )
273+ CheckVal (iter , nowtime );
243274 });
244275 }
245276
@@ -260,6 +291,46 @@ void closeTimeTask() {
260291 void closeTimeTaskAndClear () {
261292 closeTimeTask ();
262293 clear ();
294+ getGcThread ().shutdown ();
295+ }
296+
297+ /*--------------------------------------------------------------------------------------------*/
298+
299+ /**
300+ * 检查并移除已经过期的数据
301+ *
302+ * @since 0.0.2
303+ */
304+ private
305+ void CheckVal (Iterator <Map .Entry <K , Enty <V >>> iter , long nowtime ) {
306+ // 当前节点
307+ var v = iter .next ();
308+ var val = v .getValue ();
309+ // 检查是否最大时间
310+ if (val .maxtime <= nowtime ) {
311+ // 移除
312+ iter .remove ();
313+ // 异步执行回收事件
314+ GcVal (val );
315+ }
316+ }
317+
318+ /**
319+ * 申请执行当前数据的回收事件
320+ *
321+ * @since 0.0.2
322+ */
323+ private
324+ void GcVal (Enty <V > v ) {
325+ if (v == null )
326+ return ;
327+ // 当前回收事件
328+ val endrun = v .getEndrun ();
329+ // 线程池执行
330+ getGcThread ().submit (() -> {
331+ if (endrun != null )
332+ endrun .accept (v .getVal ());
333+ });
263334 }
264335
265336 /*--------------------------------------------------------------------------------------------*/
@@ -317,7 +388,7 @@ TimeMapCache<K, V> build() {
317388 * <h2>数据记录对象.</h2>
318389 *
319390 * @author fybug
320- * @version 0.0.1
391+ * @version 0.0.2
321392 * @since TimeMapCache 0.0.1
322393 */
323394 @ AllArgsConstructor
@@ -329,5 +400,13 @@ class Enty<V> {
329400 public long maxtime ;
330401 /** 数据 */
331402 @ Nullable public V val ;
403+ /**
404+ * 回收时的处理
405+ * <p>
406+ * 接口传入当前数据
407+ *
408+ * @since 0.0.2
409+ */
410+ @ Nullable public Consumer <V > endrun ;
332411 }
333412}
0 commit comments