'Green threads' (user-space scheduled threads, cheap to create) keep being reinvented in different languages. Erlang processes in 1986, goroutines in 2009, Java virtual threads in 2023. Each iteration learned from the previous; the pattern has converged on a few key ideas.
Erlang processes — the original
Tiny isolated processes (KBs of memory). Per-process heap (no shared state). Message passing. Preemptive scheduling. Built for telecom: millions of concurrent connections, hot code upgrade. Underpinned the whole BEAM/OTP ecosystem.
Goroutines — Go's bet
Stack starts small (4-8KB), grows as needed. Cooperative scheduling at function calls and channel ops. M:N scheduler (M goroutines on N OS threads). Built for network services; the 'just spawn a goroutine per request' pattern works at million-connection scale.
Virtual threads — Java's catch-up
Java 21+. Pre-existing thread API (java.lang.Thread) but cheap. Each VT is ~8KB. Carrier threads (real OS threads) execute multiple VTs. Goal: 'thread per request' programming model without OS thread cost.
The converged design
Small starting stack. M:N scheduling. Cooperative yield points at I/O. Optional preemption for long CPU-bound runs. The ergonomic 'just spawn one' model. Once-radical ideas now baseline expectations.
What's still different
Erlang: full isolation (one process crash doesn't affect others). Go: shared memory, channels for communication. Java VT: shared memory with no message passing primitive — different model. Each picks a point on the safety/ergonomics curve.