The terminology confused me at first too.
Basically it's just another word for "waiting for the thread to shutdown."
In C++, and in the Win32 world, when you created a thread, it returned a handle which was also a wait (event) handle.
So when you called a "WaitForSingleObject" on it, it would block the CPU until the thread exited and the handle was signaled.
Join is basically a wrapper around that functionality. Waiting for the thread to shutdown and signal the handle that it's done. My guess is if you look at threads as branching (execution separates here - create a thread), that the join is the opposite of that. Have the branch (the thread) join the main execution further along at this point.
Edit-
Most of the time you can specify a timeout for WaitForSingleObject, like 0, and it will just check to see if the event is signaled. My guess is that your Join command will have an override with a timeout. So you can also use Join to see if your thread has exited correctly by checking the handle.