Actually, there are needs for multiple threads to run at the same time, so the system can
waiting for all parties are ready before start.
For example, if we want to measure how long the tests take when all threads run "Concurrently' (see the example in the book "Java Concurrent in Practice" by Brian Goetz etc.
The join only works for waiting 1 thread. If you have 5-10 threads, join is not the good option.
You can use CountDownLatch as gate or CyclicBarrier for this purpose. Here is the example from the above mentioned book.
public long timeTasks(int nThreads, final Runnable task) throws InterruptedException {
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endtGate = new CountDownLatch(nThreads);
for (int i = 0; i < nThreads; i++) {
Thread t = new Thread() {
public void run() {
try {
startGate.await();
try {
task.run();
} finally {
endtGate.countDown();
}
} catch (InterruptedException ignored) {}
}
};
t.start();
}
long start = System.nanoTime();
startGate.countDown();
endtGate.await();
long end = System.nanoTime();
return end-start;
}