Describe the actor model of concurrency: what an actor consists of, and why processing one message at a time removes the need for locks around an actor's state.
answer
- private state + mailbox + behaviour
- one message at a time = mutual exclusion by construction
- only three actions: send, create, change behaviour
- no shared memory, asynchronous send, never block a handler
- millions of actors multiplexed onto a small pool
basics
~20 sAn actor is an isolated unit with private state, a mailbox, and behaviour. Actors never share memory; they only send each other asynchronous messages. Each actor processes its mailbox strictly one message at a time, so its state is never touched concurrently and needs no lock.
solid answer
~60 sAn actor has three parts: **private state** no other actor can reach, a **mailbox** (an incoming message queue), and **behaviour** — the code that handles one message. Actors communicate only by sending asynchronous messages: send is fire-and-forget, the sender does not block or wait for a result. The key invariant is **single-threaded message processing**: an actor handles exactly one message to completion before starting the next. So although thousands of actors run concurrently across a small pool of threads, any individual actor's state has only one thread of control touching it. Mutual exclusion is structural rather than something you remember to write, so there are no locks to forget, no lock ordering to get wrong, and no lock-based deadlock inside an actor. In handling a message an actor may do three things: send messages to actors it knows, create new actors, and change its own behaviour/state for the next message. The cardinal sin is blocking inside a handler — the actor cannot process anything else while blocked, and it occupies a pooled thread.
code
text · 8 linesactor Counter:
state count = 0 // reachable only here
on message Increment(n):
count = count + n // no lock: one message at a time
on message GetCount(replyTo):
send replyTo, Count(count) // asynchronous, no return valuego deeper
Give the three parts and the one-message-at-a-time rule, and conclude that private state plus serialized processing means no locks.
Add the scheduling picture — many actors multiplexed over a small pool — and why blocking inside a handler is harmful; mention request/reply being modelled as two messages.
Discuss what the model trades away: per-actor serialization as a throughput ceiling, lost stack traces, and hazards moving to delivery guarantees and mailbox growth.
Position actors against alternatives (channels, stateless workers plus a database) and discuss when entity-per-actor modelling with sharding and supervision is worth its operational complexity.
## The model The actor model is a concurrency model in which the unit of computation is an **actor**: an entity with state that nobody else can touch, an address other actors can send to, and a mailbox where those messages queue up. There is no shared memory between actors, no shared mutable objects, and therefore nothing to lock. An actor consists of: - **Private state** — variables owned exclusively by this actor. Not reachable by reference from outside; not guarded by a lock because it does not need one. - **A mailbox** — a queue of incoming messages, filled asynchronously by any sender. - **Behaviour** — a handler that processes one message and may change what the handler does next. In response to a single message an actor can do exactly three things (the classic formulation): **send** a finite number of messages to addresses it knows, **create** a finite number of new actors, and **designate** the behaviour it will use for the next message. Everything an actor system does is composed of those primitives. ## Why no locks are needed The defining rule is that an actor processes **one message at a time, to completion**. The runtime guarantees it never runs a given actor's handler on two threads simultaneously. Since the state is reachable only through the handler, and the handler is effectively single-threaded, every classic shared-state hazard inside the actor disappears: no data race on the state, no torn read of a two-field invariant, no need to reason about which lock protects which field. This is mutual exclusion by construction. It is worth stating the contrast: with threads and locks, correctness depends on every programmer remembering to take the right lock in the right order every time; with actors, the runtime enforces serialization and there is nothing to forget. The failure modes move elsewhere (overflow, ordering, protocol errors) but the classic race is designed out. ## Concurrency without a thread per actor Actors are cheap because they are not threads. A runtime multiplexes millions of actors over a pool sized near the core count: when an actor has messages, the scheduler picks it up, runs the handler for one message (or a small batch), then releases the thread. An idle actor consumes memory for state and mailbox, and nothing else. That model has a hard requirement: **handlers must not block**. If a handler waits on a synchronous network call, a lock, or a sleep, it holds a pool thread while doing nothing and the actor's own mailbox stalls behind it. Real systems either forbid blocking in handlers or isolate blocking work onto a separate, dedicated pool. ## What the model gives you beyond lock-freedom - **Location transparency.** Since interaction is only by message to an address, the recipient can be in-process or on another machine. That is what makes actor runtimes a natural distribution fabric — with the caveat that remote sending has failure modes local sending does not. - **Encapsulated failure.** An actor that crashes takes its own state down, not the process; a supervisor decides what to do next. - **Natural modelling.** Anything with identity and state over time — a connection, a game entity, a shopping cart, a device — maps directly onto an actor. ## Where actors are not magic Serialization is per actor, so a single actor is a single-threaded bottleneck: throughput on one entity is capped by one core, and hot entities must be sharded across many actors. Asynchronous messaging also loses the call stack, so 'who caused this' becomes a tracing problem rather than a stack trace. And the ordering and delivery guarantees are weaker than a method call — a topic in its own right. ## Interview delivery Name the three parts (private state, mailbox, behaviour), state the one-message-at-a-time rule and derive lock-freedom from it, mention that actors are multiplexed over a small thread pool so blocking in a handler is the cardinal sin, and close with a one-line note that the hazards move to message ordering, delivery and mailbox growth rather than vanishing.
- If actors never share memory, how does one actor get a result from another?By protocol, not by return value. The requester includes its own address in the message, and the responder sends a reply message back; the requester correlates it with the original request and applies a timeout, since no reply may ever arrive. Many runtimes wrap this as an 'ask' that yields a future, but underneath it is still two one-way messages.
- What happens if a message handler performs a blocking network call?The actor cannot process any other message until it returns, so its mailbox backs up, and because actors share a small thread pool it also removes a worker thread from the whole system. Enough blocked handlers starve unrelated actors. The remedy is to make the call asynchronous, or to run blocking work on a separate dedicated pool and deliver the result back as a message.
An actor is a clerk in a private office with a single in-tray. Nobody may reach into the office; you slide a request into the tray. The clerk handles one slip at a time, so the files on the desk are never contested.
saying these in an interview costs you the question
- Believing every actor has its own OS thread
- Assuming send blocks until the recipient processes the message, like a method call
- Sharing a mutable object by putting a reference to it in a message and continuing to modify it
- Thinking actors eliminate all concurrency bugs rather than trading them for ordering, delivery and overflow issues
- Doing blocking I/O or long computation inside a handler