Caching in Java: Redis, Memcached, and Built-in Solutions from A to Z

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Caching in Java: Redis, Memcached, and Built-in Solutions: from A to Z



Caching is one of the key tools for improving the performance of Java applications. It reduces response time, lowers the load on databases and external APIs, and improves the user experience. In this article, we will cover three main approaches to caching in Java: built-in solutions (ConcurrentHashMap, Caffeine, Ehcache), distributed systems (Redis), and lightweight network caches (Memcached). You will learn when and what to choose, and get ready-made code examples.



What is caching and why is it needed in Java?



Caching is the temporary storage of frequently requested data in a fast storage medium (usually RAM). In Java, this is especially relevant for:

  • Reducing latency — data from the cache is returned in microseconds, not milliseconds (as from a database).
  • Reducing resource load — databases, external services, and disks work less.
  • Saving resources — fewer CPU and I/O operations.

Key cache metrics: hit ratio (percentage of successful accesses) and TTL (time-to-live for an entry).



Built-in Solutions: from Simple to Advanced



For small projects or single-threaded scenarios, simple data structures can be used. However, for industrial applications, specialized libraries are better suited.



1. ConcurrentHashMap — basic cache

The simplest way is to use ConcurrentHashMap. It is thread-safe but lacks automatic eviction mechanisms (TTL, eviction).

import java.util.concurrent.ConcurrentHashMap;

public class SimpleCache { private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();

public void put(String key, Object value) { cache.put(key, value); }

public Object get(String key) { return cache.get(key); }

public void evict(String key) { cache.remove(key); }

public void clear() { cache.clear(); }}

Pros: simplicity, zero dependencies.
Cons: no automatic invalidation, manual memory management.



2. Caffeine — modern high-performance cache

The Caffeine library is the de facto standard for in-memory caching in Java. It supports:

  • Automatic eviction by time (TTL) and size.
  • Asynchronous loading.
  • Statistics (hit rate, miss rate).
import com.github.benmanes.caffeine.cache.Cache;import com.github.benmanes.caffeine.cache.Caffeine;import java.util.concurrent.TimeUnit;

public class CaffeineExample { public static void main(String[] args) { Cache<String, String> cache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000) .recordStats() // for collecting statistics .build();

// Adding cache.put("user:1", "Alice");

// Retrieval with automatic loading String value = cache.get("user:2", key -> loadFromDB(key)); System.out.println(value); }

private static String loadFromDB(String key) { // Simulating loading from DB return "Bob"; }}

Pros: high performance, flexibility, small footprint (about 600 KB).
Cons: local only (not distributed).



3. Ehcache — powerful in-process cache

Ehcache is a mature library (part of Hibernate). It supports: