Skip to main content
On this page
← All Problems/low-level-design

Synchronized Lock Contention on High-Frequency Shared Counters

System Design Interview•~30 min read•medium

Global API rate-limiting token tracker servicing 150,000 QPS across 64 CPU core server nodes.

1. Understand Requirements & Scope

Global API rate-limiting token tracker servicing 150,000 QPS across 64 CPU core server nodes. Failure cascade dynamic: 50k Mut. → Row X-Lock → Spinlock Thrash → Deadlock Graph → 💥 DB Freeze.

Functional Requirements:

  • Operational Invariant: "High-write concurrent counters must partition across CPU cache lines (Striped64/LongAdder) to eliminate cache-line ping- pong."
  • Production Hardening: Migrate to LongAdder or Striped64 cells, which dynamically distribute counter state across multiple independent CPU cache-line padded cells (@Contended), aggregating sum only on re
  • Prevent cascading failure by isolating downstream degradation at the ingress boundary.

2. Back-of-the-Envelope Capacity Estimation

• Throughput Target: 5,000,000 counter increments/sec across 64 CPU hardware threads.
• Contention Cliff: A single Java synchronized / AtomicLong shared variable causes cache line bouncing (MESI protocol invalidation) across CPU L1/L2 caches, dropping throughput by 85%.
• Memory Overhead: LongAdder striped cells allocate 128 bytes per cell with @Contended padding to prevent false sharing.
• Precision SLA: Eventual consistency on sum() reads; zero lost increments on concurrent write paths.
• CPU Utilization: CAS loops with exponential backoff prevent 100% CPU core spinning.

3. High-Level Architecture Flow

High-frequency shared counters under extreme multi-threaded concurrency suffer from MESI cache-line invalidation storms when multiple cores modify the same memory address. By striping the counter into an array of isolated 64-bit cells padded against CPU cache lines (LongAdder pattern), each thread increments its own hashed cell without cross-core lock contention.

[ 64 Concurrent CPU Threads ]
      |        |        |        |
      v        v        v        v  (Hash Thread ID -> Cell)
   +------+ +------+ +------+ +------+
   |Cell 0| |Cell 1| |Cell 2| |Cell 3|  (Striped 64-byte Padded Cells)
   +------+ +------+ +------+ +------+
      \        |        |        /
       +-------+--------+-------+
                   |  (Read Path sum())
                   v
             [ Total Count ]

Fig 1. Architecture data flow and component topology.

4. Production Implementation & Code

// High-Throughput Striped Counter with Cache-Line Padding to Prevent False Sharing
import java.util.concurrent.atomic.AtomicLong;

public class StripedCounter {
    // 64-byte padding to prevent CPU cache line false sharing (MESI protocol invalidation)
    private static class Cell {
        volatile long p0, p1, p2, p3, p4, p5, p6; // 56 bytes padding
        volatile long value;                      // 8 bytes value = 64 bytes cache line
        volatile long a0, a1, a2, a3, a4, a5, a6; // 56 bytes post-padding
        
        void add(long x) { value += x; }
    }

    private final Cell[] cells;
    private final int mask;

    public StripedCounter(int stripeCountPowerOfTwo) {
        this.cells = new Cell[stripeCountPowerOfTwo];
        for (int i = 0; i < stripeCountPowerOfTwo; i++) {
            this.cells[i] = new Cell();
        }
        this.mask = stripeCountPowerOfTwo - 1;
    }

    public void increment() {
        int hash = (int) (Thread.currentThread().getId() ^ (Thread.currentThread().getId() >>> 16));
        Cell cell = cells[hash & mask];
        synchronized (cell) {
            cell.add(1);
        }
    }

    public long sum() {
        long total = 0;
        for (Cell cell : cells) {
            total += cell.value;
        }
        return total;
    }
}

Hardened Implementation Sequence

  1. Ingress Boundary Defense: Migrate to LongAdder or Striped64 cells, which dynamically distribute counter state across multiple independent CPU cache-line padded cells (@Contende
  2. Failure Containment: Eliminate unconstrained buffering and propagate physical backpressure.
  3. Invariant Enforcement: High-write concurrent counters must partition across CPU cache lines (Striped64/LongAdder) to eliminate cache-line ping- pong.

5. Key Decisions & Operational Mitigations

Architectural Decisions & Trade-Offs

  • •Ingress Admission: Naive unconstrained buffering causes queue bloat and GC exhaustion. Mitigation: Bounded ingress gate with fast non-blocking shedding.
  • •Resource Strategy: Static capacity allocations collapse under brownouts. Mitigation: Dynamic adaptive limits scaling to real-time physical bounds.
  • •Outage Impact: Cascading lock convoys and memory saturation. Mitigation: Isolated failure domains keep P99 latency flat and protected.

Operational Hazards & Mitigations

  • •Cascading Failure: Using Java 'synchronized' or AtomicLong on a single global variable. 64 CPU cores fiercely contend on the same cache line via MESI cache coherency protocol, causing L3 cache invalidation storms and burning 85% CPU cycles on spinlock bus contention.
  • •Breakdown Chain: 50k Mut. → Row X-Lock → Spinlock Thrash → Deadlock Graph → 💥 DB Freeze
  • •Shedding Failure: Without fast rejection at the boundary, queued dead work triggers multi-second GC pauses, CPU thrashing, and cascading 504 Gateway Timeouts.
Interactive Knowledge Check • Question 1 of 3↓
Interactive Practice Quiz•Question 1 of 3
advanced

Core Architectural Principles of JVM Architecture, Memory Model & Garbage Collection

What is the primary benefit of applying JVM Architecture, Memory Model & Garbage Collection in production systems?

© 2026 abstractalgorithms.dev • Brutalist Editorial Architecture

Designed for clarity • Zero bloat