For me, Join() behavior was always confusing because I was trying to remember who will wait for whom. Don't try to remember it that way.
Underneath, it is pure wait() and notify() mechanism.
We all know that, when we call wait() on any object(t1), calling object(main) is sent to waiting room(Blocked state).
Here, main thread is calling join() which is wait() under the covers. So main thread will wait until it is notified. Notification is given by t1 when it finishes it's run(thread completion).
After receiving the notification, main comes out of waiting room and proceeds it's execution.