有这么一个要求,实现一个缓存,缓存的key在5分钟之后过期,清除可以在另一个线程中做。
实现思路:
- 一个Map<K, V>保存缓存
- 一个Queue用来保存put操作,并记录每个put动作所发生的时间
- 在对缓存put的时候,不仅对Map<K, V> put,也对Queue add。那么这个Queue就变成了一个按照时间顺序存放的队列。
- 弄一个线程定时
- peek Queue
- 如果Queue是空的,啥都不做
- 如果头元素记录的时间已经距离当前时间超过5分钟,那么就remove它,然后重复第一步
- 如果不是,则结束
代码实现如下:
ExpiryPolicy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class ExpiryPolicy {
private long ttlMillis;
private Cache cache;
private ConcurrentLinkedQueue<Node> writeQueues = new ConcurrentLinkedQueue<>();
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
public ExpiryPolicy(long ttlMillis, Cache cache) {
this.ttlMillis = ttlMillis;
this.cache = cache;
}
public void writeNode(Node node) {
writeQueues.add(node);
}
public void start() {
executor.scheduleWithFixedDelay(new ExpiringTask(),
ttlMillis, ttlMillis, TimeUnit.MILLISECONDS);
}
class ExpiringTask implements Runnable {
@Override
public void run() {
System.out.println("Start purge expired keys");
while (true) {
long now = System.currentTimeMillis();
Node node = ExpiryPolicy.this.writeQueues.peek();
if (node == null || now - node.getWriteTimestamp() < ttlMillis) {
break;
}
System.out.println("Remove expired key: " + node.getKey());
ExpiryPolicy.this.writeQueues.remove(node);
ExpiryPolicy.this.cache.remove(node.getKey());
}
}
}
}
|
Cache:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class Cache<K, V> {
private ConcurrentHashMap<K, Node<K, V>> cacheMap = new ConcurrentHashMap<>();
private ExpiryPolicy expiryPolicy;
public void put(K key, V value) {
Node<K, V> node = new Node<>(key, value);
cacheMap.put(key, node);
expiryPolicy.writeNode(node);
}
public V remove(K key) {
Node<K, V> node = cacheMap.remove(key);
return node == null ? null : node.getValue();
}
public V get(K key) {
Node<K, V> node = cacheMap.get(key);
return node == null ? null : node.getValue();
}
public void setExpiryPolicy(ExpiryPolicy expiryPolicy) {
this.expiryPolicy = expiryPolicy;
}
}
|
Node:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class Node<K, V> {
private K key;
private V value;
private long writeTimestamp;
public Node(K key, V value) {
this.key = key;
this.value = value;
this.writeTimestamp = System.currentTimeMillis();
}
// getters, equals, hashCode
}
|
用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class CacheTest {
@Test
public void test() throws InterruptedException {
Cache<String, Integer> cache = new Cache<>();
ExpiryPolicy expiryPolicy = new ExpiryPolicy(TimeUnit.SECONDS.toMillis(5), cache);
cache.setExpiryPolicy(expiryPolicy);
expiryPolicy.start();
cache.put("foo", 1);
cache.put("bar", 2);
assertEquals(cache.get("foo"), Integer.valueOf(1));
assertEquals(cache.get("bar"), Integer.valueOf(2));
TimeUnit.SECONDS.sleep(8L);
assertEquals(cache.get("foo"), null);
assertEquals(cache.get("bar"), null);
}
}
|
相关代码在这里。
评论