Skip to content

Commit 0aeb7bc

Browse files
时间缓存增加回收接口
1 parent 55d7ce4 commit 0aeb7bc

5 files changed

Lines changed: 104 additions & 25 deletions

File tree

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ tasks.withType(JavaCompile) {
88
}
99

1010
group 'fybug.nulll'
11-
version = '0.0.2'
11+
version = '0.0.2 expand 1'
1212
sourceCompatibility = '14'
1313
targetCompatibility = '14'
1414

jar/PDCache.jar

-6.74 KB
Binary file not shown.

jar/PDCache_bin.jar

-6.74 KB
Binary file not shown.

lib/PDConcurrent.jar

10.8 KB
Binary file not shown.

src/main/java/fybug/nulll/pdcache/memory/TimeMapCache.java

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,33 @@
22
import org.jetbrains.annotations.NotNull;
33
import org.jetbrains.annotations.Nullable;
44

5+
import java.util.Iterator;
56
import java.util.LinkedHashMap;
67
import java.util.Map;
78
import java.util.Timer;
89
import java.util.TimerTask;
10+
import java.util.concurrent.ExecutorService;
11+
import java.util.concurrent.Executors;
12+
import java.util.function.Consumer;
913

1014
import fybug.nulll.pdconcurrent.ObjLock;
1115
import fybug.nulll.pdconcurrent.SyLock;
1216
import lombok.AllArgsConstructor;
1317
import lombok.Getter;
18+
import lombok.NonNull;
1419
import lombok.Setter;
1520
import 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/>
@@ -34,7 +44,7 @@
3444
* </ul>
3545
*
3646
* @author fybug
37-
* @version 0.0.1
47+
* @version 0.0.2
3848
* @since memory 0.0.2
3949
*/
4050
public
@@ -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

Comments
 (0)