Oh I'm not a thread expert by any means.
In my simplistic view of the world

sleep will pause a thread (move it from a running to a blocking state) until a specified time has elapsed.
join will also pause a thread but it is used to block as you wait for another thread to end (move to a dead state). There's also a combo variation that will pause while you wait on another thread to die but after a maximum duration, the thread moves back to a runnable state.
In other words, sleep is used if you just want to wait for a while and join is used if you started another thread to do some processing and you don't want to continue until that thread is done.
You might use sleep if you want to show a splash screen when your application starts up. You might use join if you started a printjob thread and one that spins an hourglass cursor and you want the main process thread to wait until the printjob finishes to signal the hourglass to stop.
There's also a yield method that puts your thread back into the ready state - essentially giving up the current running state and signaling to the scheduler that another ready-thread has the chance to jump in (if there is one).
Corrections or additions are welcome,
- Dave