
在Java程序中,有时会有这样的需求:在主线程中启动了一个或多个子线程,需要主线程等待这些子线程执行完毕后,再继续执行。那么,Java main如何等待呢? 主要有以下几种方式:
1、使用Thread类的join()方法。
2、使用CountDownLatch类。
3、使用CyclicBarrier类。
4、使用Future类。
5、使用ExecutorService类的invokeAll()方法。
现在,我们来详细介绍一下这几种方法。
一、使用Thread类的join()方法
join()是Thread类中的一个实例方法,它的主要作用就是让调用该方法的线程等待该线程终止。也就是说,如果我们在主线程中启动了一个子线程,然后在主线程中调用了这个子线程的join()方法,那么主线程就会等待这个子线程执行完毕后,再继续执行。例如:
Thread thread = new Thread(() -> {
// 子线程需要执行的代码
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 以上代码中的子线程执行完毕后,下面的代码才会执行
这种方式最简单,也最常用。但是,它有一个缺点,就是必须等待当前线程执行完毕后,才能调用下一个线程的join()方法,也就是说,这种方式无法实现同时等待多个线程。
二、使用CountDownLatch类
CountDownLatch是java.util.concurrent包中的一个类,它可以让一个线程等待其他多个线程。具体做法是,我们在主线程中创建一个CountDownLatch对象,构造方法的参数是需要等待的线程数。然后,在每个子线程中,执行完毕后调用CountDownLatch对象的countDown()方法,这样,主线程就会等待所有子线程都调用了countDown()方法后,再继续执行。例如:
int threadNum = 5;
CountDownLatch latch = new CountDownLatch(threadNum);
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
// 子线程需要执行的代码
latch.countDown();
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 以上代码中的所有子线程执行完毕后,下面的代码才会执行
这种方式可以实现同时等待多个线程,而且代码相对简单,但是它有一个缺点,就是无法获取子线程的返回值。
三、使用CyclicBarrier类
CyclicBarrier也是java.util.concurrent包中的一个类,它的功能和CountDownLatch类似,都可以让一个线程等待其他多个线程。但是,CyclicBarrier更强大一些,因为它可以重复使用。具体做法是,我们在主线程中创建一个CyclicBarrier对象,构造方法的参数是需要等待的线程数,以及一个可选的Runnable对象(当所有线程都达到屏障时,首先执行这个Runnable对象的run()方法)。然后,在每个子线程中,执行完毕后调用CyclicBarrier对象的await()方法,这样,主线程就会等待所有子线程都调用了await()方法后,再继续执行。例如:
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, () -> {
// 所有线程都达到屏障后,首先执行这个代码
});
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
// 子线程需要执行的代码
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
// 以上代码中的所有子线程执行完毕后,下面的代码才会执行
这种方式可以实现同时等待多个线程,而且可以重复使用,但是它也有一个缺点,就是无法获取子线程的返回值。
四、使用Future类
Future是java.util.concurrent包中的一个接口,它可以表示异步计算的结果。具体做法是,我们在主线程中创建一个ExecutorService对象,然后使用这个对象的submit()方法提交一个Callable对象,这个Callable对象就是子线程需要执行的代码。submit()方法会返回一个Future对象,我们可以通过这个Future对象获取子线程的返回值,而且还可以通过Future对象的get()方法让主线程等待子线程执行完毕。例如:
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
// 子线程需要执行的代码,返回一个整数
return 123;
});
try {
Integer result = future.get();
// 以上代码中的子线程执行完毕后,下面的代码才会执行
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
这种方式可以获取子线程的返回值,而且代码相对简单,但是它有一个缺点,就是无法同时等待多个线程。
五、使用ExecutorService类的invokeAll()方法
invokeAll()是ExecutorService类中的一个方法,它可以让主线程等待所有子线程执行完毕。具体做法是,我们在主线程中创建一个ExecutorService对象,然后使用这个对象的invokeAll()方法提交一个Callable对象的列表,这些Callable对象就是子线程需要执行的代码。invokeAll()方法会返回一个Future对象的列表,我们可以通过这些Future对象获取子线程的返回值,而且还可以通过Future对象的get()方法让主线程等待子线程执行完毕。例如:
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Callable<Integer>> tasks = new ArrayList<>();
for (int i = 0; i < 5; i++) {
tasks.add(() -> {
// 子线程需要执行的代码,返回一个整数
return 123;
});
}
try {
List<Future<Integer>> futures = executor.invokeAll(tasks);
// 以上代码中的所有子线程执行完毕后,下面的代码才会执行
} catch (InterruptedException e) {
e.printStackTrace();
}
这种方式可以同时等待多个线程,而且可以获取子线程的返回值,但是代码相对复杂一些。
以上就是Java main如何等待的几种方式,每种方式都有其优点和缺点,可以根据具体需求选择合适的方式。
相关问答FAQs:
1. 如何在Java的main方法中等待其他线程完成?
在Java的main方法中,可以使用join方法来等待其他线程完成。在需要等待的线程对象上调用join方法,这会导致当前线程(即main线程)等待,直到该线程执行完毕。
2. 在Java中,如何实现主线程等待子线程完成任务?
要实现主线程等待子线程完成任务,可以使用CountDownLatch类。在主线程中创建一个CountDownLatch对象,并将计数器初始化为子线程的数量。然后在每个子线程的任务完成时,调用CountDownLatch的countDown()方法来减少计数器。最后,在主线程中调用CountDownLatch的await()方法,这会导致主线程等待,直到计数器为0。
3. 在Java中,如何实现主线程等待异步任务的完成?
要实现主线程等待异步任务的完成,可以使用CompletableFuture类。在主线程中创建一个CompletableFuture对象,并使用CompletableFuture的supplyAsync方法来执行异步任务。然后,在主线程中调用CompletableFuture的get方法,这会导致主线程等待,直到异步任务完成并返回结果。
注意:以上方法都是等待其他线程或任务完成,以便主线程能够继续执行下一步操作。
文章包含AI辅助创作,作者:Edit2,如若转载,请注明出处:https://docs.pingcode.com/baike/350586