operating systems30 min

Threads

Lightweight units of execution within a process

0/9Not Started

Why This Matters

A process can do one thing at a time on its own. But what if a web server needs to handle thousands of requests simultaneously? Or a video editor needs to render frames while keeping the UI responsive? That is where threads come in. Threads are lightweight units of execution that live inside a process and share its memory, allowing multiple tasks to run concurrently within the same program.

Threads are how modern software achieves responsiveness and throughput. Every web browser uses threads to load images while you scroll. Every database uses threads to serve multiple queries at once. But sharing memory introduces dangerous pitfalls: race conditions, deadlocks, and corrupted data. Understanding threads and thread safety is essential for writing correct, concurrent programs.

Define Terms

Visual Model

ProcessContainer
Thread 1main
Thread 2worker
Thread 3worker
Stack 1
Stack 2
Stack 3
Shared Heap Memory

The full process at a glance. Click Start tour to walk through each step.

Threads share heap memory but each has its own stack. This shared-memory model is both powerful and dangerous.

Code Example

Code
// JavaScript is single-threaded, but uses Web Workers
// for true parallelism in browsers

// Main thread (main.js)
const worker = new Worker("worker.js");

// Send data to the worker thread
worker.postMessage({ numbers: [1, 2, 3, 4, 5] });

// Receive results from the worker thread
worker.onmessage = (event) => {
  console.log("Sum from worker:", event.data.sum);
};

// Worker thread (worker.js)
// self.onmessage = (event) => {
//   const sum = event.data.numbers.reduce((a, b) => a + b, 0);
//   self.postMessage({ sum });
// };

// Node.js: Worker Threads
const { Worker, isMainThread, parentPort } = require("worker_threads");

if (isMainThread) {
  const worker = new Worker(__filename);
  worker.on("message", (msg) => {
    console.log("Result:", msg); // 55
  });
  worker.postMessage(10);
} else {
  parentPort.on("message", (n) => {
    let sum = 0;
    for (let i = 1; i <= n; i++) sum += i;
    parentPort.postMessage(sum);
  });
}

Interactive Experiment

Try these exercises:

  • In Python, remove the with lock: line from the example above and run it. Does the counter reach 200? Run it several times and compare results.
  • In a browser console, run a long while loop. Notice how the page freezes because JavaScript is single-threaded.
  • Create a Web Worker in the browser to perform a heavy calculation. Does the main page stay responsive?
  • In Python, compare threading vs multiprocessing for a CPU-bound task. Which is faster?

Quick Quiz

Coding Challenge

Detect the Race Condition

The function `unsafeBankTransfer` simulates two concurrent transfers that read and write a shared balance. Due to interleaved execution, the final balance is wrong. Write a function `safeBankTransfer` that produces the correct result by ensuring transfers happen atomically. Simulate atomicity by processing transfers sequentially. The function takes an initial balance and an array of transfer amounts, and should return the final balance.

Loading editor...

Real-World Usage

Threads power every high-performance system:

  • Web browsers use a main thread for DOM rendering and separate threads for network requests, image decoding, and JavaScript execution (Web Workers).
  • Database servers like PostgreSQL use a thread (or process) per connection to handle multiple queries simultaneously.
  • Game engines use threads for rendering, physics simulation, audio, and AI, all running in parallel.
  • Node.js uses a thread pool internally (via libuv) for file system operations, DNS lookups, and crypto even though your JavaScript runs on a single thread.
  • Python web servers like Gunicorn use multiple worker processes (not threads) to bypass the GIL and achieve true parallelism.

Connections