Key Considerations and Implementation Strategies for a Local Cache in Java
This article outlines the essential design considerations for building a local cache—such as data structures, size limits, eviction policies, expiration, thread safety, simple APIs, persistence, and blocking mechanisms—and demonstrates concrete Java implementations with code examples.
When examining MyBatis source code, the cache subsystem reveals both first‑level and second‑level caches, with the latter offering richer functionality; this article uses that insight to discuss what should be considered when implementing a local cache.
Considerations
The main points include how data is stored, the maximum amount of data, handling of excess entries, expiration, thread safety, a simple interface, and optional persistence.
1. Data Structure
The simplest approach is to use a Map (e.g., ConcurrentHashMap ), while more complex systems like Redis employ various structures such as hashes, lists, sets, and sorted sets backed by linked lists, compressed lists, and skip lists.
2. Object Limit
Local caches run in memory, so a configurable upper bound (e.g., 1024 entries) is typical; once the limit is reached, a removal strategy must be applied.
3. Eviction Strategies
Common policies include LRU (Least Recently Used), FIFO (First‑In‑First‑Out), LFU (Least Frequently Used), SOFT (soft references), and WEAK (weak references), each implemented with appropriate Java collections or reference types.
4. Expiration Time
Caches often support per‑key TTL, which can be enforced via passive checks during get/put operations or active background jobs that periodically purge expired entries.
5. Thread Safety
Because multiple threads may access the cache concurrently, thread‑safe containers like ConcurrentHashMap or explicit synchronization (e.g., MyBatis’s SynchronizedCache ) are required.
6. Simple Interface
A user‑friendly API typically provides get , put , remove , clear , and getSize methods, as illustrated by MyBatis’s Cache interface.
7. Persistence
Persisting cache entries to disk enables recovery after a restart; frameworks like Ehcache support disk persistence, while Redis offers AOF and RDB mechanisms.
8. Blocking Mechanism
To avoid duplicate expensive computations, a blocking cache can lock a key while a value is being loaded, allowing other threads to wait rather than repeat the work.
Implementation
Below are concrete Java snippets that demonstrate each consideration.
1. Data Structure
Map
cache = new ConcurrentHashMap<>();2. Object Limit
Define a maximum size (e.g., 1024) and enforce it when inserting new entries.
3. Eviction Strategies
LRU can be realized with LinkedHashMap , FIFO with a Queue , LFU by tracking access counts in a HashMap , SOFT with SoftReference , and WEAK with WeakReference .
4. Expiration (Passive)
if (System.currentTimeMillis() - lastClear > clearInterval) {
clear();
}5. Thread Safety
public synchronized void putObject(Object key, Object object) { ... }
public synchronized Object getObject(Object key) { ... }6. Simple Interface
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();
}7. Persistence Example
diskPersistent="false" // whether to persist to disk8. Blocking Mechanism Example
public class Memoizer
implements Computable
{
private final Map
> cache = new ConcurrentHashMap<>();
private final Computable
c;
public Memoizer(Computable
c) { this.c = c; }
public V compute(A arg) throws InterruptedException, ExecutionException {
while (true) {
Future
f = cache.get(arg);
if (f == null) {
Callable
eval = () -> c.compute(arg);
FutureTask
ft = new FutureTask<>(eval);
f = cache.putIfAbsent(arg, ft);
if (f == null) { f = ft; ft.run(); }
}
try { return f.get(); }
catch (CancellationException e) { cache.remove(arg, f); }
}
}
}Conclusion
The design of a local cache should address data structures, size limits, eviction policies, expiration, thread safety, blocking, a clean API, and optional persistence; additional considerations may arise depending on specific use cases.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.