Concurrency and Multithreading in JavaScript: Advanced Level

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Concurrency and Multithreading in JavaScript: Advanced Level



JavaScript has historically been a single-threaded language operating within the Event Loop. However, modern Node.js applications require high performance when handling CPU-intensive tasks. In this article, we will explore advanced concurrency and multithreading mechanisms: Worker Threads, SharedArrayBuffer, Atomics, and clustering. You will learn how to efficiently use multi-core processors and avoid blocking the main thread.



1. Worker Threads: True Multithreading in Node.js



The worker_threads module appeared in Node.js 10.5.0 and allows creating separate threads with their own V8 engine. Unlike clustering (processes), Worker Threads share memory and can exchange data via SharedArrayBuffer.



Creating and Running a Worker



// main.jsconst { Worker } = require('worker_threads');

function runWorker(data) { return new Promise((resolve, reject) => { const worker = new Worker('./worker.js', { workerData: data }); worker.on('message', resolve); worker.on('error', reject); worker.on('exit', (code) => { if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`)); }); });}

(async () => { const result = await runWorker({ numbers: [1, 2, 3, 4, 5] }); console.log('Result:', result); // { sum: 15 }})();


// worker.jsconst { parentPort, workerData } = require('worker_threads');

const sum = workerData.numbers.reduce((acc, val) => acc + val, 0);parentPort.postMessage({ sum });


Important: Workers do not have access to the DOM or modules requiring the main thread. Use them for heavy computations, image processing, or encryption.



2. SharedArrayBuffer and Atomics: Shared Memory



SharedArrayBuffer allows multiple workers to read and write data to the same memory area. The Atomics object is used for synchronization access — it guarantees atomic operations.



Example: Parallel Array Processing



// main.jsconst { Worker } = require('worker_threads');const { SharedArrayBuffer } = require('worker_threads');

const buffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 10);const array = new Int32Array(buffer);

// Fill the arrayfor (let i = 0; i < 10; i++) array[i] = i;

const worker1 = new Worker('./worker_atomics.js', { workerData: { start: 0, end: 5 } });const worker2 = new Worker('./worker_atomics.js', { workerData: { start: 5, end: 10 } });

worker1.on('message', () => console.log('Worker 1 finished'));worker2.on('message', () => console.log('Worker 2 finished'));


// worker_atomics.jsconst { parentPort, workerData } = require('worker_threads');const { Atomics } = require('worker_threads');

const buffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 10);const array = new Int32Array(buffer);

// Atomic increment of each elementfor (let i = workerData.start; i < workerData.end; i++) { Atomics.add(array, i, 10);}

parentPort.postMessage('done');


Note: Use Atomics.wait() and Atomics.notify() for thread synchronization (analogous to mutexes). This is critical for preventing race conditions.



3. Clustering: Scaling via Processes



The cluster module creates multiple Node.js processes (workers), each handling requests. This is the standard way to scale HTTP servers on multi-core systems.



Example: Clustered HTTP Server



const cluster = require('cluster');const http = require('http');const numCPUs = require('os').cpus().length;

if (cluster.isMaster) { console.

Blogs

Book Recommendations