Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
Chaturmind
← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
HomeLearnSystem DesignLow-Level Design & Design PatternsMachine Coding Exercises
✓ FreeAdvanced· 7 min read

Thread-Safe LRU Cache

Building an LRUCache class from LinkedHashMap, adding correct thread safety with ReadWriteLock's actual limitation exposed, and eviction callback hooks.

Published September 23, 2026


Thread-Safe LRU Cache

TreeMap & LinkedHashMap already covered the single-threaded removeEldestEntry() LRU trick. This lesson is the natural follow-up interviewers ask: make it thread-safe.

Base implementation via LinkedHashMap

class LRUCache<K, V> {
    private final int capacity;
    private final LinkedHashMap<K, V> cache;

    LRUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new LinkedHashMap<>(16, 0.75f, true) { // accessOrder = true
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; }
        };
    }

    V get(K key) { return cache.get(key); }
    void put(K key, V value) { cache.put(key, value); }
}

Adding thread safety: why a plain synchronized method isn't quite enough

class ThreadSafeLRUCache<K, V> {
    private final LinkedHashMap<K, V> cache; // as above
    private final ReentrantLock lock = new ReentrantLock();

    V get(K key) {
        lock.lock();
        try { return cache.get(key); } finally { lock.unlock(); }
    }
    void put(K key, V value) {
        lock.lock();
        try { cache.put(key, value); } finally { lock.unlock(); }
    }
}

The subtlety worth naming explicitly: get() on an access-ordered LinkedHashMap is not read-only — it mutates the internal linked-list ordering (moving the accessed entry to the most-recently-used position). This means get() can't just be wrapped in a shared read lock the way a genuinely read-only operation could; it needs the same exclusive lock as put(), because it's structurally a write.

Would ReadWriteLock help here? No — and that's the actual interview insight

// LOOKS reasonable, IS INCORRECT for this specific case:
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

V get(K key) {
    rwLock.readLock().lock(); // WRONG — get() mutates access order, this isn't a true read
    try { return cache.get(key); } finally { rwLock.readLock().unlock(); }
}

ReadWriteLock (see synchronized and Locks) is the right tool specifically when reads genuinely don't mutate shared state, allowing multiple concurrent readers. Here, get() does mutate state (the LRU ordering) — using a shared read lock for it would let two threads concurrently call get() and race on updating the same internal linked-list pointers, corrupting the LRU order (or, in pathological cases, LinkedHashMap's internal structure itself). This is exactly the kind of question where an interviewer expects you to recognize why a seemingly-obvious optimization (ReadWriteLock for a "read" operation) is actually wrong for this specific data structure's true read/write semantics — a plain exclusive ReentrantLock (or synchronized) is the correct answer here, not a premature optimization mistake.

Eviction callback hooks

interface EvictionListener<K, V> { void onEvict(K key, V value); }

class ThreadSafeLRUCache<K, V> {
    private final EvictionListener<K, V> evictionListener;
    private final LinkedHashMap<K, V> cache;

    ThreadSafeLRUCache(int capacity, EvictionListener<K, V> evictionListener) {
        this.evictionListener = evictionListener;
        this.cache = new LinkedHashMap<>(16, 0.75f, true) {
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                boolean shouldEvict = size() > capacity;
                if (shouldEvict) evictionListener.onEvict(eldest.getKey(), eldest.getValue());
                return shouldEvict;
            }
        };
    }
}

A common, genuinely useful follow-up extension: notify something when an entry gets evicted (e.g. releasing a resource the cached value held, like a file handle or a pooled connection — connecting directly to Connection Pool Design's own resource-lifecycle concerns). removeEldestEntry() is exactly the right hook point, since it's already called precisely once per eviction, with the evicted entry passed in directly.

Follow-up questions this topic invites — and their answers

Q: Could ConcurrentHashMap be used instead of a lock-wrapped LinkedHashMap? A: Not directly for LRU specifically — ConcurrentHashMap has no built-in access-order/eviction concept the way LinkedHashMap does; achieving LRU semantics with it would require building the ordering structure (a doubly-linked list) yourself alongside it, which is considerably more implementation work than wrapping LinkedHashMap in a single lock.

Q: Why might a single global lock become a bottleneck, and what's the fix? A: Under high concurrent read/write load, a single lock serializes every operation regardless of which keys are involved — a common mitigation is sharding the cache into N independent LRUCache instances, each with its own lock, keyed by hash(key) % N (the same sharding idea as HashMap Concurrency Variants' segment-based ConcurrentHashMap predecessor), trading perfect global LRU ordering for much better concurrent throughput.

Q: What happens if EvictionListener.onEvict() throws an exception? A: Since it's called from inside removeEldestEntry(), which LinkedHashMap calls from inside put(), an uncaught exception there would propagate up through put() itself — a production implementation should catch and log exceptions from the listener internally, so a misbehaving eviction callback can't break the cache's own put() operation.

Q: Is accessOrder=true always the right choice, or are there cases you'd want insertion order instead? A: LRU specifically requires access order (recency of use, not creation) — but if the goal were a different eviction policy like FIFO (evict oldest-inserted regardless of access pattern, see the Distributed Cache case's eviction policy comparison), insertion order (the LinkedHashMap default, accessOrder=false) would be the correct choice instead.

Previous

In-Memory Rate Limiter

Next

Connection Pool Design

AI Tutor

Lesson: Thread-Safe LRU Cache

Quick actions

AI responses can be inaccurate. Verify critical information.