If I use threadB.wait() in threadA and in threadB I do not call notify(), when the run() method of threadB finishes, then the threadA continues like I had call notify() in threadB...
This is an implementation detail -- which is of course, subject to change.
The join() method is implementated internally with the wait()/notifyAll(). A join() method call on the thread object does a wait() on the thread object. When the thread finishes, some of things that it does are, set the alive flag to indicate that it is not, and send a notifyAll() to all threads doing a join().
Obviously, you are not using the join() method, but you are waiting on the same notification object used by the join() method.