Interview frequency: …
Short answer
Thread-per-Request assigns a full thread to each user connection and reserves it while waiting. An Event Loop uses one thread to listen for socket events through kernel mechanisms (epoll/kqueue) without blocking.
Explanation
In classic servers such as older Apache Tomcat, reserving one thread per request means 10,000 concurrent connections require 10,000 threads, consuming several gigabytes for stacks and collapsing under context-switch overhead (the C10K problem). Node.js, Nginx, and Netty instead use one Event Loop: register the client socket with the kernel, and when network bytes arrive, the kernel sends a notification for the Event Loop to handle with a fast callback before listening again.
Code example
// Event loop (Node.js): one thread, non-blocking I/O — the thread is free while the DB works
import http from "node:http";
http.createServer(async (req, res) => {
const rows = await db.query("SELECT * FROM products");
res.end(JSON.stringify(rows));
}).listen(3000);
// Thread-per-request (classic Java): every connection occupies a whole thread while it waits
ServerSocket server = new ServerSocket(3000);
while (true) {
Socket client = server.accept();
new Thread(() -> handle(client)).start(); // blocked on the DB → threads pile up under load
}Common mistakes
- Running a heavy CPU-bound task (such as complex encryption or mining) on the Event Loop thread, freezing requests for all other users.