Understanding Caffeine: A High‑Performance Java Caching Library
This article introduces the Caffeine Java caching library, detailing its features, core classes and parameters, various loading strategies—including manual, automatic, and asynchronous loading—eviction policies, removal listeners, and cache statistics, accompanied by practical code examples for integration in backend applications.
Understanding Caffeine
Caffeine is a Java‑8 based high‑performance caching library that offers near‑optimal hit rates and is the default local cache implementation in Spring Boot. It provides flexible builders to create caches with automatic loading (optional async), size‑based eviction, time‑based expiration, asynchronous refresh, weak/soft references for keys and values, eviction notifications, and access statistics.
Automatic loading of entries (optional asynchronous)
Size‑based eviction
Expiration after write or access
Asynchronous refresh
Weakly referenced keys and weak/softly referenced values
Eviction notifications
Cache access statistics
Core classes include Caffeine for building caches and parameters such as maximumSize , maximumWeight , initialCapacity , expireAfterWriteNanos , expireAfterAccessNanos , and refreshAfterWriteNanos .
Data Loading Strategies
Caffeine supports four loading strategies:
Manual loading
public static void demo() {
Cache
cache = Caffeine.newBuilder()
.expireAfterAccess(Duration.ofMinutes(1))
.maximumSize(100)
.recordStats()
.build();
cache.put("a", "a");
String a = cache.getIfPresent("a");
System.out.println(a);
String b = cache.get("b", k -> {
System.out.println("begin query ..." + Thread.currentThread().getName());
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("end query ...");
return UUID.randomUUID().toString();
});
System.out.println(b);
cache.invalidate("a");
}Automatic loading
public static void demo() {
LoadingCache
loadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader
() {
@Override public String load(@NonNull String key) { return createExpensiveValue(); }
@Override public @NonNull Map
loadAll(@NonNull Iterable
keys) {
Map
map = new HashMap<>();
for (String key : keys) { map.put(key, createExpensiveValue()); }
return map;
}
});
String a = loadingCache.get("a");
System.out.println(a);
Set
keys = new HashSet<>();
keys.add("a"); keys.add("b");
Map
all = loadingCache.getAll(keys);
System.out.println(all);
}
private static String createExpensiveValue() {
System.out.println("begin query ..." + Thread.currentThread().getName());
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("end query ...");
return UUID.randomUUID().toString();
}Manual asynchronous loading
public static void demo() throws ExecutionException, InterruptedException {
AsyncCache
asyncCache = Caffeine.newBuilder()
.maximumSize(100)
.buildAsync();
asyncCache.put("a", CompletableFuture.completedFuture("a"));
CompletableFuture
a = asyncCache.getIfPresent("a");
System.out.println(a.get());
CompletableFuture
b = asyncCache.get("b", k -> createExpensiveValue("b"));
System.out.println(b.get());
asyncCache.synchronous().invalidate("a");
System.out.println(asyncCache.getIfPresent("a"));
}
private static String createExpensiveValue(String key) {
System.out.println("begin query ..." + Thread.currentThread().getName());
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("end query ...");
return UUID.randomUUID().toString();
}Automatic asynchronous loading
public static void demo() throws ExecutionException, InterruptedException {
AsyncLoadingCache
cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.buildAsync((key, executor) -> createExpensiveValueAsync(key, executor));
CompletableFuture
a = cache.get("a");
System.out.println(a.get());
Set
keys = new HashSet<>();
keys.add("a"); keys.add("b");
CompletableFuture
> values = cache.getAll(keys);
System.out.println(values.get());
}
private static CompletableFuture
createExpensiveValueAsync(String key, Executor executor) {
System.out.println("begin query ..." + Thread.currentThread().getName());
try { Thread.sleep(1000); executor.execute(() -> System.out.println("async create value....")); } catch (InterruptedException e) {}
System.out.println("end query ...");
return CompletableFuture.completedFuture(UUID.randomUUID().toString());
}Eviction Strategies
Caffeine offers three eviction strategies: size‑based, time‑based, and reference‑based, plus manual removal and listeners.
Size‑based eviction using .maximumSize() or .maximumWeight()
Time‑based eviction via .expireAfterAccess() , .expireAfterWrite() , or a custom Expiry implementation
Reference‑based eviction with .weakKeys() , .weakValues() , and .softValues()
Manual removal can be performed with cache.invalidate(key) , cache.invalidateAll(keys) , or cache.invalidateAll() . Removal listeners can be registered via .removalListener() or .evictionListener() to react to eviction causes such as EXPLICIT, REPLACED, COLLECTED, EXPIRED, or SIZE.
Cache Statistics
Enabling .recordStats() allows collection of detailed metrics such as hit count, miss count, load success/failure counts, total load time, eviction count, and request count.
CacheStats stats = cache.stats();
System.out.println("stats.hitCount():" + stats.hitCount());
System.out.println("stats.hitRate():" + stats.hitRate());
System.out.println("stats.missCount():" + stats.missCount());
System.out.println("stats.missRate():" + stats.missRate());
System.out.println("stats.loadSuccessCount():" + stats.loadSuccessCount());
System.out.println("stats.loadFailureCount():" + stats.loadFailureCount());
System.out.println("stats.totalLoadTime():" + stats.totalLoadTime());
System.out.println("stats.evictionCount():" + stats.evictionCount());
System.out.println("stats.requestCount():" + stats.requestCount());
System.out.println("stats.averageLoadPenalty():" + stats.averageLoadPenalty());JD Tech Talk
Official JD Tech public account delivering best practices and technology innovation.
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.