Concurrency

How different programming languages approach concurrent and parallel execution.

JavaScript

Runs on a single-threaded event loop with non-blocking I/O; concurrency is expressed through Promises and async/await. True parallelism requires separate Web Workers or Node’s worker_threads, since there’s no shared-memory threading by default.

Python

The Global Interpreter Lock (GIL) limits the threading module’s usefulness for CPU-bound parallelism, so multiprocessing is used for true parallel execution. asyncio provides cooperative, single-threaded concurrency via async/await.

TypeScript

Layers typed Promises and async function signatures on top of JavaScript’s event-loop model, but introduces no new concurrency primitives of its own — the runtime behavior is identical to JavaScript.

Java

Built on native OS threads, with java.util.concurrent providing executors, locks, and atomic types. CompletableFuture composes asynchronous pipelines, and recent versions add virtual threads (Project Loom) for much lighter-weight concurrency.

C#

Centers on the Task-based Asynchronous Pattern with async/await and the Task Parallel Library (TPL), backed by a managed thread pool. Async streams and concurrent collections (System.Collections.Concurrent) round out the model.

C++

Offers std::thread, std::mutex/condition_variable, and task-based concurrency via std::async/futures/promises. Atomics and a formal memory model have been standardized since C++11.

C

Provides no concurrency primitives itself; concurrent programs rely on platform threading APIs like POSIX threads (pthreads) and manual synchronization with mutexes and condition variables.

PHP

Traditionally a single-threaded, share-nothing, one-process-per-request model. True threading is available only through extensions (e.g., pthreads) and is rarely used; modern async workloads instead rely on event-loop libraries (ReactPHP, Swoole) or native Fibers (PHP 8.1+).

Go

Goroutines are cheap, runtime-scheduled concurrent functions, communicating through channels following a CSP-style model. select multiplexes over channel operations, and the sync package offers lower-level primitives like mutexes and wait groups.

Rust

The ownership and borrowing system enforces “fearless concurrency” at compile time via the Send/Sync traits. Supports OS threads (std::thread) directly, plus async/await backed by external runtimes like Tokio, along with channels and locks in the standard library.

Kotlin

Coroutines are the primary concurrency model: suspend functions, structured concurrency scoped by coroutine builders, and Flow for asynchronous streams. Coroutines interoperate with underlying Java threads but avoid blocking them.

Ruby

Threads exist but are constrained for CPU-bound work by the Global VM Lock (GVL). Fibers provide lightweight cooperative concurrency, while Ractors offer true parallelism with isolated state; async libraries often build cooperative concurrency on top of fibers.

Dart

Uses a single-threaded event loop with async/await/Future/Stream for cooperative concurrency, so code within an isolate never races on shared memory. True parallelism comes from isolates — independent workers with their own memory that communicate only via message passing.

Swift

Modern Swift uses structured concurrency with async/await, Task, and task groups, plus actors that protect shared mutable state from data races. Grand Central Dispatch (GCD) remains the underlying (and still-used legacy) queue-based system.

Perl

Has threads via the threads module, but they are heavyweight (each thread duplicates the interpreter’s data) and rarely used in practice. More commonly, concurrency is achieved with forked processes or event-loop libraries such as AnyEvent or POE.

Elixir

Runs on the BEAM VM’s lightweight, isolated processes that communicate purely through message passing rather than shared memory. OTP behaviors like GenServer and Supervisor structure concurrent, fault-tolerant systems, allowing millions of cheap concurrent processes.

Scala

Runs on the JVM and can use raw threads directly, but idiomatically relies on Futures for asynchronous composition and richer effect/actor libraries — Akka for actor-based concurrency, and ZIO or Cats Effect for structured, functional effect systems.

Clojure

Provides atoms, refs, and agents as managed alternatives to raw locks for coordinating shared state, plus software transactional memory for refs. core.async adds CSP-style channels and go blocks for asynchronous, sequential-looking code, all running on top of JVM threads.

Haskell

The GHC runtime manages lightweight, cheaply-forked green threads via forkIO, scheduled cooperatively across OS threads. Safe shared state uses MVar for simple synchronization or STM (software transactional memory) for composable atomic transactions, with the async library providing higher-level concurrency combinators.