Perl has had two threading implementations: "5.005 threads" and "interpreter threads". They both use the OS's underlying threading facilities; they differ in whether perl data structures are shared by default.
in reply to what the history behind perl not having "real" threads
5.0005 threads (introduced with perl 5.005) by default shared all data and data structures. This turned out to be almost impossible to make thread-safe, since almost any perl-level "read" operation can actually end up modifying an SV (scalar value). For example:
my $x = 1;
print $x; # whoops $x has been modified: converted from int to string
$y = \$x; # whoops $x has been modified: its ref count has increased.
To get this to work right would involve locking before just about any operation. So that threads model was abandoned.
There was a separate effort to allow fork emulation under Windows (which doesn't support fork()). This worked by collecting all perl's state into a single interpreter struct and allocating all SVs from per-interpreter pools. When fork() was called, the interpreter and all its SVs etc etc would be copied, and a new thread created which ran using that new data. So each "process" (actually just a thread) had its complete own copy of everything and could run independently without affecting any other threads; no (or very little) locking required. This first appeared with 5.6.
Then someone had the idea of exposing this interface at the perl level (rather than just via fork() under windows). Thus was born the threads.pm module, which did a similar thing to the fork (cloned the current state), but started the new thread with fresh code rather than running from the same point as the caller (a la fork()). Someone also added threads::shared, which via a mechanism similar to tying, allowed data structures to be shared across threads.
These came out with 5.8.