Java判断线程池中的任务是否执行完毕
1. shutdown + isTerminated
当调用ExecutorService.shutdown方法的时候,线程池不再接收任何新任务,但此时线程池并不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。
在调用shutdown方法后我们可以在一个死循环里面用isTerminated方法判断是否线程池中的所有线程已经执行完毕。
public class MyThreadPoolExecutor {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
4,
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
new ThreadPoolExecutor.CallerRunsPolicy());
int size = 10;
for (int i = 0; i < size; i++) {
int num = i;
executor.submit(() -> {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + ", " + num);
});
}
// ============ 判断线程池中的任务是否执行完毕 ============
executor.shutdown();
while (true) {
if (executor.isTerminated()) {
System.out.println("===> 执行完毕 isTerminated");
break;
}
}
}
}
2. awaitTermination
当使用awaitTermination时,主线程会处于一种等待的状态,等待线程池中所有的线程都运行完毕后才继续运行。
在以下任意一种情况发生时都会导致该方法的执行:
1)shutdown方法被调用之后
2)参数中定义的timeout时间到达或者当前线程被打断
这两情况任意一个发生了都会导致该方法在所有任务完成之后才执行。第一个参数是long类型的超时时间,第二个参数可以为该时间指定单位。
public class MyThreadPoolExecutor {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
4,
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
new ThreadPoolExecutor.CallerRunsPolicy());
int size = 10;
for (int i = 0; i < size; i++) {
int num = i;
executor.submit(() -> {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + ", " + num);
});
}
// ============ 判断线程池中的任务是否执行完毕 ============
executor.shutdown();
executor.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
System.out.println("===> 执行完毕 awaitTermination");
}
}
3. countDownLatch
判断线程池中的线程是否全部执行完毕的另外一种解决方案则是使用闭锁(CountDownLatch)来实现,CountDownLatch是一种灵活的闭锁实现,它可以使一个或多个线程等待一组事件发生。闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示需要等待的事件数量。
countDown方法递减计数器,表示有一个事件已经发生了,而await方法等待计数器达到零,即表示需要等待的事情都已经发生。可以使用闭锁来这样设计程序达到目的:
public class MyThreadPoolExecutor {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
4,
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
new ThreadPoolExecutor.CallerRunsPolicy());
int size = 10;
// 计时器
CountDownLatch countDownLatch = new CountDownLatch(size);
for (int i = 0; i < size; i++) {
int num = i;
executor.submit(() -> {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + ", " + num);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 一定要写在 finally中,防止异常计数器未减1
// 使latch的值减1,如果减到了0,则会唤醒所有等待在这个latch上的线程
countDownLatch.countDown();
}
});
}
// ============ 判断线程池中的任务是否执行完毕 ============
countDownLatch.await();
System.out.println("===> 执行完毕 countDownLatch");
}
}
总结
这三种方法都可以用来判断线程池中的任务是否结束,但推荐的方式取决于你的具体需求和代码结构。
1、isTerminated方法:这是ExecutorService接口提供的方法之一,用于判断线程池是否已经终止。它返回一个布尔值,指示所有任务是否已经完成,线程池是否已经关闭。这种方法简单直接,适用于不需要等待所有任务完成的情况。
2、awaitTermination方法:这是ExecutorService接口提供的方法之一,用于等待线程池中的所有任务执行完成或者等待超时。你可以设置一个超时时间,使得在超过指定时间后,不再等待任务的完成而继续执行后续的代码。这种方法适用于需要等待所有任务完成的情况,而且你可以控制等待的最大时间。
3、CountDownLatch:CountDownLatch是Java并发包中提供的一种同步工具,用于在一个或多个线程完成任务之前等待。你可以初始化一个CountDownLatch对象,并将其计数器设置为任务的数量,然后在每个任务执行完成时调用countDown方法来减少计数器的值。最后,可以调用await方法来等待计数器变为0。这种方法适用于你需要在某些特定任务完成之后再继续执行后续的代码。
总的来说,如果你只是需要判断线程池中的任务是否结束,那么直接使用isTerminated方法就足够了。如果你需要等待所有任务完成,可以使用awaitTermination方法。如果你需要更复杂的等待逻辑,例如在某些任务完成后才能继续执行,那么可以考虑使用CountDownLatch。
参考:
https://www.cnblogs.com/mask-xiexie/p/16501118.html