Threads (Concurrency)
In short: A thread is an independent path of execution within a program — several threads can run seemingly or actually simultaneously, sharing the program’s memory and resources while doing so.
In more detail: Threads allow, for example, letting a computationally intensive task run in the background while the user interface keeps responding. The biggest challenge is that several threads accessing the same data at the same time can lead to race conditions (unpredictable results depending on execution order) — synchronisation mechanisms that controllably restrict concurrent access help against this.
In Depth
A race condition arises when two threads read AND write the same variable at the same time, and the final result depends on the exact order in which that happens — a classic example:
# counter starts at 0, two threads execute SIMULTANEOUSLY:
# Thread A: Thread B:
value = counter value = counter # both read 0
value = value + 1 value = value + 1 # both compute 0+1=1
counter = value counter = value # both write 1
# Expected: counter = 2 (two increments)
# Actual: counter = 1 (one increment was "lost")Even though each individual thread executes correct code, interleaving the execution order loses one increment — and the problem doesn’t reliably occur on every program run, which makes race conditions one of the hardest error types to find and reproduce at all.
The classic solution is a lock (also called a mutex): a thread first has to “secure” exclusive access to a critical spot in the code before entering it — every other thread has to wait until the lock is released again:
lock.acquire()
counter = counter + 1 # only ONE thread can enter this block at a time
lock.release()Locks solve race conditions, but bring their own risks: a deadlock arises when two threads are each waiting for a lock the other currently holds — both stay blocked forever, because neither gives way. A typical trigger is locking several locks in different orders; the common avoidance strategy is to always maintain a fixed, consistent lock order throughout the entire codebase.
Modern languages often offer higher-level abstractions that avoid many of these pitfalls from the outset — such as immutable data structures (which can’t suffer a race condition, because they’re never changed), thread-safe collections, or entirely different concurrency models like message passing instead of shared memory.
See also: Recursion, Algorithms