Using Caffeine: A Java Local Cache Framework – Features, Code Samples, and Performance Tips

This article introduces the Caffeine Java caching library, explains its flexible expiration and write policies, and provides detailed manual, synchronous, and asynchronous code examples with console output to demonstrate how to integrate and benchmark Caffeine for high‑performance backend services.

FunTester
FunTester
FunTester
Using Caffeine: A Java Local Cache Framework – Features, Code Samples, and Performance Tips

In Java development, Caffeine is regarded as the most powerful native caching framework, built on the concepts of Guava Cache but with optimized algorithms for better performance.

Introduction

Caffeine is a native Java cache library that also works with Groovy, offering high‑performance in‑process caching.

Common Features

The author mainly uses three Caffeine features:

Flexible expiration policies, including access‑time expiration, write‑time expiration, and custom strategies.

Flexible write policies, supporting manual, synchronous, and asynchronous writes.

A simple API that is quick to learn.

Advanced features are not covered here; performance data will be tested later with JMH.

Feature Demonstration

The following sections show practical implementations of the three write strategies.

Manual Write

import com.funtester.frame.SourceCode
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import groovy.util.logging.Log4j2
import java.util.concurrent.TimeUnit
import java.util.function.Function

@Log4j2
class CaffeineManual extends SourceCode {
    static void main(String[] args) {
        Cache<Integer, Integer> cache = Caffeine.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(100, TimeUnit.MILLISECONDS)
                .recordStats()
                .build()

        int key = 1
        log.info("无缓存返回: {}", cache.getIfPresent(key))
        log.info("无缓存自定义返回: {}", cache.get(key, new Function<Integer, Integer>() {
            @Override
            Integer apply(Integer integer) {
                return 3
            }
        }))
        cache.put(key, 2)
        log.info("手动赋值后返回: {}", cache.getIfPresent(key))
        sleep(1.0)
        log.info("缓存过期返回: {}", cache.getIfPresent(key))
        cache.put(key, 2)
        cache.invalidate(key)
        log.info("手动删除后返回: {}", cache.getIfPresent(key))
    }
}

Console output:

21:41:30.329 main 无缓存返回: null
21:41:30.337 main 无缓存自定义返回: 3
21:41:30.338 main 手动赋值后返回: 2
21:41:31.360 main 缓存过期返回: null
21:41:31.364 main 手动删除后返回: null

Synchronous Write

import com.funtester.frame.SourceCode
import com.github.benmanes.caffeine.cache.CacheLoader
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache
import groovy.util.logging.Log4j2
import java.util.concurrent.TimeUnit

@Log4j2
class CaffeineSync extends SourceCode {
    static int cacheInit(int key) {
        log.info("返回赋值: {}", key)
        return key * 100
    }

    static void main(String[] args) {
        LoadingCache<Integer, Integer> cache = Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.MINUTES)
                .maximumSize(100)
                .build(new CacheLoader<Integer, Integer>() {
                    @Override
                    Integer load(Integer integer) throws Exception {
                        return cacheInit(integer)
                    }
                })
        Integer value = cache.get(1)
        log.info("无缓存返回: {}", value)
        log.info("自定义返回: {}", cache.get(2, { return 31 }))
        log.info("获取返回结果: {}", value)
        Map<Integer, Integer> resMap = cache.getAll([1, 2, 3])
        log.info("批量返回: {}", resMap)
    }
}

Console output:

21:54:54.900 main 返回赋值: 1
21:54:54.903 main 无缓存返回: 100
21:54:54.963 main 自定义返回: 31
21:54:54.963 main 获取返回结果: 100
21:54:54.964 main 返回赋值: 3
21:54:54.965 main 批量返回: {1=100, 2=31, 3=300}

Asynchronous Load

import com.funtester.frame.SourceCode
import com.funtester.frame.execute.ThreadPoolUtil
import com.github.benmanes.caffeine.cache.AsyncCache
import com.github.benmanes.caffeine.cache.Caffeine
import groovy.util.logging.Log4j2
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import java.util.function.Function

@Log4j2
class CaffeineAsync extends SourceCode {
    static int cacheInit(int key) {
        return key * 100
    }

    static void main(String[] args) {
        AsyncCache<Integer, Integer> asyncCache = Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.SECONDS)
                .maximumSize(100).executor(ThreadPoolUtil.getFunPool()).buildAsync()
        CompletableFuture<Integer> future = asyncCache.get(1, new Function<Integer, Integer>() {
            @Override
            Integer apply(Integer integer) {
                log.info("开始加载缓存")
                sleep(1.0)
                return cacheInit(integer)
            }
        })
        log.info("FunTester1")
        sleep(2.0)
        log.info("FunTester2")
        log.info("异步加载返回: {}", future.get())
        sleep(2.0)
        log.info("缓存过期后Future返回: {}", future.get())
        log.info("缓存过期后cache返回: {}", asyncCache.getIfPresent(1))
        log.info("无缓存返回: {}", asyncCache.getIfPresent(2))
    }
}

Console output:

22:13:06.728 main FunTester1
22:13:06.728 F-1   开始加载缓存
22:13:08.738 main FunTester2
22:13:08.747 main 异步加载返回: 100
22:13:10.748 main 缓存过期后Future返回: 100
22:13:10.749 main 缓存过期后cache返回: null
22:13:10.750 main 无缓存返回: null

Key observations:

The loading logic runs in the CompletableFuture before get completes.

After expiration, CompletableFuture can still return the previously loaded value, while asyncCache.getIfPresent returns null.

The author notes that the latest Caffeine version no longer supports JDK 8; the used version is:

compile group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.9.3'

Overall, the article demonstrates that Caffeine can be mastered within half an hour, covering manual, synchronous, and asynchronous write strategies with practical code snippets.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaCacheCaffeine
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.