Green threads
Threads managed by the language runtime, cheap enough to have one per task, so ordinary blocking code scales like async code.
How they work
Instead of changing how code is written, make threads cheap. The runtime multiplexes many lightweight threads onto a few OS threads; when one blocks on I/O, the runtime parks it and runs another on the same OS thread. Go’s goroutines start with a stack of a few kilobytes that grows and shrinks as needed, and the Go FAQ calls hundreds of thousands of them in one address space practical. Java 21’s virtual threads do the same for the JDK: a blocking call performs a non-blocking OS call underneath and suspends the virtual thread, which holds an OS thread only while it computes; JEP 444 runs a million of them. A new Erlang process (on the BEAM virtual machine) takes 327 words of memory.
The payoff is no colour and real stacks: every function is ordinary, and debuggers, profilers and stack traces see one request as one thread. Bob Nystrom names Go, Lua and Ruby as languages that avoid the colour problem because they can switch between independent call stacks.
The limits are those of any threads: CPU-bound work still needs cores, shared memory still needs locks (or channels and mailboxes), and a Java virtual thread that blocks inside a synchronized block or a native method stays pinned to its OS thread.
Sources
- Go FAQ: Why build concurrency on the ideas of CSP? and Why goroutines instead of threads?
- OpenJDK: JEP 444, Virtual Threads (Little’s law, OS threads are costly, the asynchronous style loses stack traces and debuggers)
- Erlang/OTP docs: Efficiency guide, Processes (a new process uses 327 words)
- Bob Nystrom: What Color is Your Function? (Feb 2015)