public synchronized void join() throws InterruptedException {
  int nThreads=runners.size();
  if (nThreads == 0)   return;
  if (!started)   start();
  semaphore.acquire(nThreads);
  semaphore.release(nThreads);
}

public synchronized void join ( ) throws InterruptedException { int nThreads = runners . size ( ) ; if ( nThreads == 0 ) return ; if ( ! started ) start ( ) ; semaphore . acquire ( nThreads ) ; semaphore . release ( nThreads ) ; }
waits for all runnable to have finished their execution . can be called multiple times : after the first time , this method always returns immediately . if the threads is not started yet , this method will start it implicitely .
