Skip to main content
Articles/Java/concurrency-models-actors-vs-csp
Scenario Article

Concurrency Models: Actor Model vs. Communicating Sequential Processes (CSP)

Concept: The Limitations of Shared-Memory Concurrency

Abstract Algorithms•Jun 18, 2026
💡 Executive Summary

Shared-memory multithreading using manual locks is notoriously difficult to scale and debug.

🎯 System Scenario & Requirements

System Goal

Shared-memory multithreading using manual locks is notoriously difficult to scale and debug.

Actors & Components

  • Application or API tier
  • Core service under design
  • Operator or SRE observer

Functional Requirements

  • TLDR: Shared-memory multithreading using manual locks is notoriously difficult to scale and debug.
  • To avoid race conditions and deadlocks, modern platforms use message-passing concurrency models.
  • This guide compares the Actor Model (used in Erlang and Akka/Pekko) with Communicating Sequential Processes (CSP, used in Go) and provides full Java implementations for both.

Constraints & Assumptions

  • In the classic multi-threaded programming model, threads communicate by sharing access to the same heap memory space.
  • To prevent concurrent threads from overwriting each other's data (race conditions) or reading partial writes (inconsistent states), developers must use synchronization primitives like locks, mutexes, or semaphores.
  • Mailbox Overflow: If an actor processes messages slower than they arrive, its mailbox will grow indefinitely, leading to an OutOfMemoryError .
  • Mitigation: Use bounded mailboxes and configure drop-oldest or dead-letter queue policies.
  • Passing Mutable Objects in Messages: Message-passing models only guarantee thread safety if messages are immutable .
  • If Actor 1 sends a mutable Java List to Actor 2, both threads will hold a reference to that list, restoring shared-memory concurrent write bugs.

⚡ Execution Stages & Decision Trace

1

Stage 1 — Mechanics: Mailboxes, Processes, and Message Passing

Action / Decision:

To implement message passing, we choose between two primary concurrency models: 1\.

Component Behavior:

The Actor Model In the Actor Model, the fundamental unit of computation is the Actor .

Observable Output: An actor is a self-contained object that encapsulates state, behavior, and a private message queue called a Mailbox .
Read Full Stage in Deep Dive ↓
📖

Deep Dive

Detailed Architectural Analysis

Scenario context

Shared-memory multithreading using manual locks is notoriously difficult to scale and debug. To avoid race conditions and deadlocks, modern platforms use message-passing concurrency models. This guide compares the Actor Model (used in Erlang and Akka/Pekko) with Communicating Sequential Processes (CSP, used in Go) and provides full Java implementations for both.


In the classic multi-threaded programming mode…

This walkthrough focuses on Concept: The Limitations of Shared-Memory Concurrency.

Before making a design choice, clarify:

  • In the classic multi-threaded programming model, threads communicate by sharing access to the same heap memory space.
  • To prevent concurrent threads from overwriting each other's data (race conditions) or reading partial writes (inconsistent states), developers must use synchronization primitives like locks, mutexes, or semaphores.
  • However, manual shared-memory locking introduces major architectural challenges: Deadlocks: If Thread 1 holds Lock A and waits for Lock B, while Thread 2 holds Lock B and waits for Lock A, both threads will hang indefin…

Concepts in play: jvm-internals-and-garbage-collection, stack-and-queue-data-structures, circuit-breaker, message-queues

Stage 1 — Mechanics: Mailboxes, Processes, and Message Passing

  • To implement message passing, we choose between two primary concurrency models: 1.
  • The Actor Model In the Actor Model, the fundamental unit of computation is the Actor .
  • An actor is a self-contained object that encapsulates state, behavior, and a private message queue called a Mailbox .
  • Actors communicate exclusively by sending asynchronous messages to other actors' mailboxes.

Observable outcome: Actors communicate exclusively by sending asynchronous messages to other actors' mailboxes.

Stage 2 — Flow: Message Passing Sequence

  • The diagram below visualizes the architectural differences between the Actor Model's address-based routing and CSP's channel-based routing: The table below summarizes the key structural differences between the two model…
flowchart TD
    subgraph Actor Model: Address-Based
        Actor1[Actor 1] -->|Send Message to Address| Mailbox2[Mailbox 2]
        Mailbox2 -->|Sequentially Process| Actor2[Actor 2]
    end

    subgraph CSP Model: Channel-Based
        Proc1[Process 1] -->|Write to Channel| Channel[Shared Channel]
        Channel -->|Read from Channel| Proc2[Process 2]
    end

Observable outcome: The diagram below visualizes the architectural differences between the Actor Model's address-based routing and CSP's channel-based routing: The table below summarizes the key structural differences between the two model…

Stage 3 — Deep Dive: Structural Isolation & Thread Context Switches

  • Let us analyze the internal queue mechanics, mathematical models, and performance constraints of message-passing concurrency.
  • Thread Isolation and Queue Internals In both models, message passing relies on thread-safe queues.
  • In the Actor Model, mailboxes are typically implemented using non-blocking queues (like Java's ConcurrentLinkedQueue ) to allow publishers to push messages without blocking.
  • In CSP, channels are implemented using blocking queues (like Java's ArrayBlockingQueue with a capacity boundary) to enforce backpressure.

Observable outcome: In CSP, channels are implemented using blocking queues (like Java's ArrayBlockingQueue with a capacity boundary) to enforce backpressure.

Stage 4 — Advanced Concepts: Supervision Trees and Channel Buffering

  • A key advantage of the Actor Model is its Supervision Model (pioneered by Erlang).
  • Actors are organized in a hierarchy.
  • If an actor throws an uncaught exception, its parent supervisor detects the failure and applies a recovery policy: One-for-One: Restart only the failed actor.
  • One-for-All: Restart all child actors in the supervisor group.

Observable outcome: One-for-All: Restart all child actors in the supervisor group.

Stage 5 — Applications: From Erlang Systems to Go Channels

  • High-Frequency Chat Backends (Erlang/Akka): Chat servers represent each user connection as a dedicated actor, managing state and messaging in isolation.
  • Go Web Server Pipelines (CSP): HTTP requests are handled by lightweight goroutines communicating via channels to execute logging, authentication, and database updates in parallel.
  • Telecommunication Switches: Managing thousands of simultaneous calls using isolated actors to prevent call failures from affecting the system.

Observable outcome: Telecommunication Switches: Managing thousands of simultaneous calls using isolated actors to prevent call failures from affecting the system.

End-to-end outcome

After working through the stages, you should be able to explain:

  • Concurrency Proverb: Share memory by communicating, rather than communicating by sharing memory.
  • Actor Model: Encapsulates state and mailbox.

Alternatives considered

Decision Guide: Shared Memory vs. Actors vs. CSP

Decision Guide: Shared Memory vs. Actors vs. CSP

Let us implement both patterns in Java.

Let us implement both patterns in Java.

1. Lightweight Actor System in Java

This implementation defines an actor structure using Java's ExecutorService and ConcurrentLinkedQueue.

2. CSP Model using BlockingQueue Channels

This implementation defines a Producer-Consumer pipeline using Java's ArrayBlockingQueue to represent a CSP channel.

Failure and degraded modes

Concept: The Limitations of Shared-Memory Concurrency

  • Detection: In the classic multi-threaded programming model, threads communicate by sharing access to the same heap memory space.
  • User or operator impact: In the classic multi-threaded programming model, threads communicate by sharing access to the same heap memory space.

Trade-offs and Failure Modes

  • Detection: Mailbox Overflow: If an actor processes messages slower than they arrive, its mailbox will grow indefinitely, leading to an OutOfMemoryError .
  • User or operator impact: Mailbox Overflow: If an actor processes messages slower than they arrive, its mailbox will grow indefinitely, leading to an OutOfMemoryError .

Lessons Learned: Common Concurrency Pitfalls

  • Detection: Passing Mutable Objects in Messages: Message-passing models only guarantee thread safety if messages are immutable .
  • User or operator impact: Passing Mutable Objects in Messages: Message-passing models only guarantee thread safety if messages are immutable .

Share this case study

Discuss architecture trade-offs and lessons with your team.

Share:XLinkedInReddit