kernel-level threads
Now imagine the opposite of the secretive manager: every employee is registered with the head office, which assigns desks and decides who works when. The head office sees each worker individually and can move them between offices. Kernel-level threads are like that: threads that the operating system itself knows about and schedules. The kernel maintains the bookkeeping for each thread and places threads onto CPU cores directly.
Concretely, with kernel-level threads the operating system keeps a record (its own thread control structure) for every thread, and the kernel's scheduler picks among threads, not just among processes, when it decides what each CPU core should run next. Creating a thread, switching between threads, and synchronizing them therefore go through the kernel via system calls, which makes each of these operations heavier than the purely in-process switching of user-level threads. Almost every general-purpose OS today — Linux, Windows, macOS — provides kernel-level threads as the foundation that the user-facing thread APIs are built on.
The payoff is exactly what user-level threads cannot do. Because the kernel schedules each thread independently, two threads of one process can run on two different cores at the same instant, giving real parallelism. And if one thread makes a blocking system call, the kernel can simply schedule a different thread of the same process onto the CPU, so the whole process does not freeze. The cost is the catch: every thread operation crosses into the kernel, so kernel threads are heavier to create and switch than user threads — which is why creating one thread per tiny task is wasteful, and why the thread pool pattern exists.
On Linux, the threads you create with pthreads map to kernel threads, so a four-thread program doing heavy math can occupy all four cores of your laptop at once. And when one of those threads blocks on a network read, the kernel keeps the other three running instead of stalling the whole program.
The kernel schedules each thread, so they get real parallelism — at a higher per-operation cost.
Kernel threads buy true parallelism and survive a blocking call by one thread, but every create, switch, and sync crosses into the kernel and so costs more than a user-level switch. That cost is why thread pools beat thread-per-task.