Concurrency Visualizer

Interactive exploration of race conditions and timing dependencies

How to Use This Tool

Quick Start

  1. 1.Choose a scenario from the five available examples (Counter, Bank Account, Singleton, Cache, Deadlock)
  2. 2.Select an execution ordering to see how different thread interleavings affect the outcome
  3. 3.Step through the execution using Play/Pause or manual Next/Previous buttons
  4. 4.Observe the state changes in both shared and thread-local state panels

Understanding the Display

Shared State (Purple)

This shows data that all threads can access. In real systems, this could be a database, shared memory, cache, or file. Race conditions occur when multiple threads read/write this state without proper synchronization.

Thread Local State (Green)

This shows data that is private to each thread. Each thread has its own copy of local variables. Notice how threads can have different local values even when they read from the same shared state at different times.

Key Concepts to Learn

Race Condition

When the outcome depends on the unpredictable timing of thread execution. The same code can produce different results on different runs.

👉 Look for: Steps marked with ⚠️ warning symbols, especially when the final state differs between execution orderings.

Lost Update Problem

When one thread's update overwrites another thread's update because both read the old value before either writes. Common in databases and concurrent modifications.

👉 Look for: Two threads reading the same initial value, then both writing back modified versions - the last write wins and the first is lost.

Check-Then-Act Pattern

Testing a condition and then acting on it in separate steps. The condition can change between the check and the act, causing bugs. Seen in singleton patterns, file operations, and caches.

👉 Look for: The singleton example where both threads check "is null?" and both see true, leading to duplicate object creation.

Deadlock

When two or more threads wait for each other to release resources, creating a circular dependency that never resolves. The system freezes completely.

👉 Look for: The deadlock example where T1 holds A and waits for B, while T2 holds B and waits for A - neither can proceed.

Timing Dependencies (Distributed Systems)

In distributed systems, operations take time to propagate. A read might happen before an update completes, leading to stale data or inconsistencies.

👉 Look for: The cache example shows delay times (+50ms, +100ms). Notice how network delays affect which data gets read.

What to Watch For

⚠️
Red warnings indicate problematic executions where race conditions cause incorrect results
Green checks indicate correct executions where proper ordering prevents issues
T1
Blue badges represent Thread 1 or Server 1 operations
T2
Orange badges represent Thread 2 or Server 2 operations

Learning Tips

  • Compare orderings: Run through all execution orderings for each scenario. Notice how the same code produces different results.
  • Focus on state transitions: Pay attention to when shared state changes and when thread-local state differs from shared state.
  • Identify the critical section: In each example, find the sequence of read-modify-write steps that need to be protected.
  • Think about real systems: Consider how these patterns apply to databases, web servers, APIs, and other systems you work with.
  • Use step-by-step mode: Don't just watch the auto-play. Manually step through to understand each state change.

Common Solutions

While this tool focuses on understanding the problems, here are typical solutions used in real systems:

Locks/Mutexes

Ensure only one thread accesses the critical section at a time

Atomic Operations

Hardware-supported operations that complete without interruption

Optimistic Locking

Detect conflicts after they occur and retry the operation

Why This Matters

This is the most fundamental concurrency bug. It appears in web analytics, inventory systems, like counters, and any shared accumulator. Even simple operations like count++ aren't atomic!

Real-world example: A website tracking page views where multiple servers increment the same counter in a database without proper locking.

Code

function incrementCounter() {
  let temp = counter;  // Read
  temp = temp + 1;     // Increment
  counter = temp;      // Write
}

Shared State

counter:0

Thread Local State

No local state yet

Execution Timeline

Step 1 of 0