-
Notifications
You must be signed in to change notification settings - Fork 4
/
TimeBasedCache.java
142 lines (118 loc) · 3.33 KB
/
TimeBasedCache.java
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package general;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class TimeBasedCache {
public static int CACHE_SIZE;
public static long TTL;
public static void main(String[] args) {
Scanner scanner = null;
try {
System.out.println("Started at " + LocalDateTime.now());
scanner = new Scanner(System.in);
System.out.print("Enter cache size : ");
CACHE_SIZE = scanner.nextInt();
System.out.print("Enter TTL (in sec): ");
TTL = scanner.nextLong();
while(true) {
String item = scanner.nextLine();
if(!item.isEmpty()) {
System.out.print("\nEnter key-value pair -> ");
String pair[] = item.split(":");
System.out.println("Inserting a key : "+pair[0]+" at " + LocalDateTime.now());
CacheUtil.CACHE.put(pair[0], pair[1]);
}
}
} catch (Exception e) {
scanner.close();
e.printStackTrace();
}
System.out.println(CacheUtil.CACHE.getCache());
}
}
enum CacheUtil {
CACHE;
private ConcurrentMap<String, Entry> cache = new ConcurrentHashMap<>();
private Object lock = new Object();
CacheUtil() {
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(2);
scheduledExecutor.scheduleAtFixedRate(new ExpiryChecker(cache, lock), 0, 1, TimeUnit.SECONDS);
}
public void put(String key, Object value) {
synchronized (lock) {
if(cache.size()>=TimeBasedCache.CACHE_SIZE) {
System.out.println("Cache is full.....Waiting for elements expiration.");
try {
System.out.println("Cache is full.....Waiting for elements expiration.");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
cache.put(key, new Entry(value, System.currentTimeMillis() + TimeBasedCache.TTL * 1000));
}
}
public void get(String key) {
cache.get(key);
}
public ConcurrentMap<String, Entry> getCache() {
return cache;
}
}
class Entry {
private Object value;
private Long ttl;
public Entry(Object value, Long ttl) {
super();
this.setValue(value);
this.setTtl(ttl);
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public Long getTtl() {
return ttl;
}
public void setTtl(Long ttl) {
this.ttl = ttl;
}
@Override
public String toString() {
return value.toString() + " - " + ttl;
}
}
class ExpiryChecker implements Runnable {
private ConcurrentMap<String, Entry> cache = new ConcurrentHashMap<>();
private Object lock;
public ExpiryChecker(ConcurrentMap<String, Entry> cache, Object lock) {
this.cache = cache;
this.lock = lock;
}
@Override
public void run() {
Set<String> keysToBeRemoved = new HashSet<>();
for(Map.Entry<String, Entry> entry : cache.entrySet()) {
Entry etr = entry.getValue();
if(etr.getTtl()<=System.currentTimeMillis()) {
keysToBeRemoved.add(entry.getKey());
}
}
for(String keyToBeRemoved : keysToBeRemoved) {
System.out.println("\nRemoving key : " + keyToBeRemoved + " at " + LocalDateTime.now());
cache.remove(keyToBeRemoved);
synchronized(lock) {
lock.notifyAll();
}
}
}
}