Poll on isAlive before calling join. This polling can be interrupted, of course, and once the thread isn't isAlive, join is immediate.
An alternative would be polling on join with a timeout, checking with isAlive whether the timeout occurred. This can spend less CPU than the previous method.