User-level threads are threads that the OS is not aware of. They exist entirely within a
process, and are scheduled to run within that process's timeslices.
The OS is aware of kernel-level threads. Kernel threads are scheduled by the OS's
scheduling algorithm, and require a "lightweight" context switch to switch between (that
is, registers, PC, and SP must be changed, but the memory context remains the same
among kernel threads in the same process).
User-level threads are much faster to switch between, as there is no context switch;
further, a problem-domain-dependent algorithm can be used to schedule among them.
CPU-bound tasks with interdependent computations, or a task that will switch among
threads often, might best be handled by user-level threads.
Kernel-level threads are scheduled by the OS, and each thread can be granted its own
timeslices by the scheduling algorithm. The kernel scheduler can thus make intelligent
decisions among threads, and avoid scheduling processes which consist of entirely idle
threads (or I/O bound threads). A task that has multiple threads that are I/O bound, or that
has many threads (and thus will benefit from the additional timeslices that kernel threads
will receive) might best be handled by kernel threads.
Kernel-level threads require a system call for the switch to occur; user-level threads do
not.