Imagine an application where a thread (typically, main) has spawned off several other worker threads. Each worker thread has a specific job to do; once done, it terminates (via pthread_exit(3)). How will the creator thread know when a worker thread is done (terminated)? Ah, that is precisely where joining comes in. With the join, the creator thread can wait for, or block upon, the death (termination) of another thread within the process!
Does this not sound very much like the wait(2) system call that a parent process issues to wait for the death of a child? True, but as we shall see shortly, it's certainly not identical.
Also, importantly, the return value from the thread that terminated is passed along to the thread that issued ...