方法一,CompletableFuture.allOf().join()

CompletableFuture.allOf()方法本身并不会返回线程池执行完毕的信息,它只会返回一个新的CompletableFuture对象,用于表示所有CompletableFuture对象都执行完成。

如果需要在CompletableFuture.allOf()方法执行完毕后获取线程池执行完毕的信息,可以在每个CompletableFuture对象中添加回调函数,在任务执行完毕后向一个共享的计数器中增加计数器的值,当所有任务执行完毕后,计数器的值将等于任务总数。这时,我们就可以判断线程池执行完毕了。

例如,假设有一个线程池,它需要执行10个任务,并使用CompletableFuture.allOf()方法等待所有任务执行完毕后进行某些操作,可以使用以下代码来判断线程池执行完毕:

// 创建一个计数器
AtomicInteger count = new AtomicInteger(0);

// 执行10个任务,并添加回调函数
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        // 执行某些操作
    });
    future.thenRun(() -> {
        // 每个任务执行完毕后,将计数器的值加1
        count.incrementAndGet();
    });
    futures.add(future);
}

// 等待所有任务执行完毕
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
allFutures.join();

// 判断线程池执行完毕
if (count.get() == 10) {
    // 线程池执行完毕,可以进行某些操作
}

这段代码创建了一个包含10个CompletableFuture对象的列表,每个对象都代表一个异步任务。在每个CompletableFuture对象执行完毕后,都会调用thenRun()方法添加一个回调函数,这个回调函数会将计数器的值加1。最后,等待所有CompletableFuture对象执行完毕后,判断计数器的值是否等于10,如果是,则说明线程池执行完毕了。
如果所有的CompletableFuture对象都是在同一个线程池中执行的,那么在allFutures.join()方法返回后,就可以认为线程池中的任务全部执行完毕了。因为CompletableFuture在执行时会使用传入的线程池来执行任务,并且在执行完任务后会将结果或异常保存在Future对象中,因此可以通过等待所有Future对象都执行完毕来判断线程池是否执行完毕。

方法二,使用ExecutorService 和 Future

在 Java 中,可以使用 ExecutorService 和 Future 接口来判断线程池中的任务是否执行完毕。下面是一个简单的示例:

ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个大小为10的线程池
List<Future<?>> futures = new ArrayList<>(); // 用于保存提交到线程池中的任务

// 提交任务到线程池中
for (int i = 0; i < 100; i++) {
    Future<?> future = executor.submit(new Runnable() {
        @Override
        public void run() {
            // 执行任务
        }
    });
    futures.add(future);
}

// 等待所有任务完成
for (Future<?> future : futures) {
    try {
        future.get(); // 等待任务执行完毕
    } catch (InterruptedException | ExecutionException e) {
        // 处理异常
    }
}

// 关闭线程池
executor.shutdown();

在这个示例中,我们首先创建了一个大小为10的线程池,然后提交了100个任务到线程池中,并使用 Future 接口保存这些任务的执行结果。最后,我们使用一个循环来等待所有任务执行完毕,通过调用 Future.get() 方法来等待每个任务的执行结果,如果任务执行过程中发生了异常,我们可以在 catch 代码块中进行处理。最后,我们调用 shutdown() 方法来关闭线程池。

需要注意的是,如果线程池中的任务发生了异常,使用 Future.get() 方法会抛出 ExecutionException 异常,需要在代码中进行处理。同时,如果任务执行时间过长,可以使用 Future.get(long timeout, TimeUnit unit) 方法来指定等待时间。

方法三,使用shutdown() 方法和 awaitTermination()

除了使用 Future 接口之外,还可以通过调用 ExecutorService 的 shutdown() 方法和 awaitTermination() 方法来判断线程池执行完毕。

ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个大小为10的线程池

// 提交任务到线程池中
for (int i = 0; i < 100; i++) {
    executor.submit(new Runnable() {
        @Override
        public void run() {
            // 执行任务
        }
    });
}

executor.shutdown(); // 等待线程池中的任务执行完毕
try {
    executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
    // 处理异常
}

在这个示例中,我们首先创建了一个大小为10的线程池,然后提交了100个任务到线程池中。接下来,我们调用 shutdown() 方法来关闭线程池,该方法会使线程池拒绝新的任务,并等待线程池中的任务执行完毕。最后,我们使用 awaitTermination() 方法来等待所有任务执行完毕,该方法会阻塞当前线程,直到线程池中的所有任务执行完毕。

需要注意的是,如果线程池中的任务执行时间过长,需要根据具体的情况调整等待时间,以免阻塞太长时间。

方法四,使用 CountDownLatch 类

另外一种判断线程池执行完毕的方法是使用 CountDownLatch 类。CountDownLatch 可以用来阻塞一个或多个线程,直到某个操作完成。

ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个大小为10的线程池
CountDownLatch latch = new CountDownLatch(100); // 创建一个计数器,初始值为100

// 提交任务到线程池中
for (int i = 0; i < 100; i++) {
    executor.submit(new Runnable() {
        @Override
        public void run() {
            // 执行任务
            latch.countDown(); // 计数器减1
        }
    });
}

try {
    latch.await(); // 等待计数器归0
} catch (InterruptedException e) {
    // 处理异常
}

在这个示例中,我们创建了一个大小为10的线程池,然后提交了100个任务到线程池中。接下来,我们创建了一个初始值为100的 CountDownLatch 计数器,每个任务执行完毕后会调用 countDown() 方法将计数器减1。最后,我们使用 await() 方法来等待计数器归0,该方法会阻塞当前线程,直到计数器的值为0。

使用 CountDownLatch 的好处是可以灵活控制任务的执行顺序和数量,但是需要注意的是,如果计数器的初始值过大,会导致等待时间过长。因此,需要根据具体情况选择合适的计数器初始值。


不熄炎
6 声望2 粉丝