有很多方法可以做到这一点。
1.加入所有生成的线程。 (哦,对了,你不想要这个,跳到 2)
这意味着保留对所有这些线程的引用:
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[ALimit]; // array to keep track of our threads, or we could use a Collection
for (int i = 0; i < ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
Thread myThread= new Thread(myThreadImplementsRunnable);
threads[i] = myThread; // remember it
myThread.start();
}
for (Thread thread : threads) {
thread.join(); // wait until the thread finishes, will return immediately if it's already finished.
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
2。使用 CountDownLatch :
一个CountDownLatch基本上是一个倒计时,线程可以等待倒计时到0。所以如果每个线程完成倒计时,main可以等待0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch finishedRunning = new CountDownLatch(ALimit); // Latch with ALimit countdowns needed to flip
for (int i = 0; i < ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
Thread myThread= new Thread(() -> {
try {
myThreadImplementsRunnable.run();
} finally {
finishedRunning.countDown(); // one less to wait for, in a finally block, so exceptions don't mess up our count
}
};
myThread.start();
}
finishedRunning.await(); // waits until the count is 0
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
3.使用 ExecutorService 并关闭:
ExecutorService 为您管理线程,您可以执行任务,然后只需等待ExecutorService 终止;
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
for (int i = 0; i < ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
executorService.execute(myThreadImplementsRunnable);
}
executorService.shutdown(); // will stop accepting new tasks, but all submitted tasks so far, will still be executed
boolean terminated = executorService.awaitTermination(3, TimeUnit.MINUTES); // we have to specify a timeout, returns a boolean which we can use to test whether it timed out or not, to maybe try and force termination
if (!terminated) {
// try and force things? Shut down anyway? log and wait some more?
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
4.使用 ExecutorService 和 futures(这真的就像再次加入所有线程,所以你可能想跳过这个):
一个ExecutorService为你管理线程,你可以提交任务,跟踪返回的Futures,然后等待每个Future的结果到达;
public static void main(String[] args) throws InterruptedException {
Set<Future<?>> futures = new HashSet<>();
ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
for (int i = 0; i < ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
Future<?> future = executorService.submit(myThreadImplementsRunnable);
futures.add(future); // remember the future, pun intended ;)
}
executorService.shutdown(); // make sure the services terminates its threads when they're no longer needed.
for (Future<?> future : futures) {
try {
future.get();
} catch (ExecutionException e) {
// task failed with an exception : e.getCause() to see which
}
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
对此的一种变体是将ExecutorService 包装在CompletionService 中,这将按照完成的顺序返回Futures:
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
CompletionService<Void> completionService = new ExecutorCompletionService<Void>(executorService);
for (int i = 0; i <= ALimit - 1; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
completionService.submit(myThreadImplementsRunnable, null);
}
executorService.shutdown(); // make sure the services terminates its threads when they're no longer needed.
for (int i = 0; i < ALimit; i++) {
Future<?> future = completionService.take();
try {
future.get();
} catch (ExecutionException e) {
// task failed with an exception : e.getCause() to see which
}
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
5.使用 CompletableFutures
Java 8 为我们带来了CompletableFutures,它允许我们使用正在运行的任务作为构建块。我们可以简单地构建一个CompletableFuture 代表我们所有的异步任务。
public static void main(String[] args) throws InterruptedException {
CompletableFuture<?>[] completableFutures = new CompletableFuture<?>[ALimit];
for (int i = 0; i <ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
completableFutures[i] = CompletableFuture.runAsync(myThreadImplementsRunnable);
}
CompletableFuture<Void> all = CompletableFuture.allOf(completableFutures);
try {
all.get(); // get the 'combined' result
} catch (ExecutionException e) {
// task failed with an exception : e.getCause() to see which
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
结论
CountDownLatch 可能是您想要的,它很简单,而且开销很小。
ExecutorService 是专业人士会使用的,它清楚地将线程和任务的概念分开,提供使用 Futures 的选项,您可以取消它并为单个任务提供异常处理。线程可以重用,线程的数量可以定制,与任务数量无关。但这一切可能只是矫枉过正。
CompletionService 非常适合当您需要在任务完成后立即处理它们。
CompletableFuture 提供CountDownLatch 的简单性,但线程是为您管理的。