判断线程池任务执行完成的方式

Thread线程是否执行完成,我们可以调用join方法然后等待线程执行完成;那在使用线程池的时候,我们如何知道线程已经执行完成了?本文就带给大家五种判断的方式:
●isTerminated() 方式,在执行 shutdown() ,关闭线程池后,判断是否所有任务已经完成。
●ThreadPoolExecutor 的 getCompletedTaskCount() 方法,判断完成任务数和全部任务数是否相等。
●CountDownLatch计数器,使用闭锁计数来判断是否全部完成。
●手动维护一个公共计数 ,原理和闭锁类似,就是更加灵活。
●使用submit向线程池提交任务,Future判断任务执行状态。

方法一:isTerminated()

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79


package pool;



import java.util.concurrent.LinkedBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;



/\*\*

\* @author 百里

\*/

public class BaiLiIsShutdownThreadPoolDemo {

/\*\*

\* 创建一个最大线程数15的线程池

\*/

public static ThreadPoolExecutor pool \= new ThreadPoolExecutor(

10,

15,

0L,

TimeUnit.MILLISECONDS,

new ArrayBlockingQueue<\>(10));

/\*\*

\* 线程执行方法,随机等待0到10秒

\*/

private static void sleepMethod(int index){

try {

long sleepTime \= new Double(Math.random() \* 10000).longValue();

Thread.sleep(sleepTime);

System.out.println("当前线程执行结束: " + index);

} catch (InterruptedException e) {

e.printStackTrace();

}

}



/\*\*

\* 方法一:isTerminated

\* @param args

\* @throws InterruptedException






上述代码处理逻辑在主线程中进行循环判断,全部任务是否已经完成。
这里有两个主要方法:
●shutdown() :对线程池进行有序关闭。调用该方法后,线程池将不再接受新的任务,但会继续执行已提交的任务。如果线程池已经处于关闭状态,则对该方法的调用没有额外的作用。
●isTerminated() :判断线程池中的所有任务是否在关闭后完成。只有在调用了shutdown()或shutdownNow()方法后,所有任务执行完毕,才会返回true。需要注意的是,在调用shutdown()之前调用isTerminated()方法始终返回false。

优缺点分析

优点 :操作简单。 缺点 :需要关闭线程池。并且日常使用是将线程池注入到Spring容器,然后各个组件中统一用同一个线程池,不能直接关闭线程池。

方法二:getCompletedTaskCount()

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79


package pool;



import java.util.concurrent.\*;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;



/\*\*

\* @author 百里

\*/

public class BaiLiIsShutdownThreadPoolDemo {

/\*\*

\* 创建一个最大线程数15的线程池

\*/

public static ThreadPoolExecutor pool \= new ThreadPoolExecutor(

10,

15,

0L,

TimeUnit.MILLISECONDS,

new ArrayBlockingQueue<\>(10));

/\*\*

\* 线程执行方法,随机等待0到10秒

\*/

private static void sleepMethod(int index){

try {

long sleepTime \= new Double(Math.random() \* 10000).longValue();

Thread.sleep(sleepTime);

System.out.println("当前线程执行结束: " + index);

} catch (InterruptedException e) {

e.printStackTrace();

}

}



/\*\*

\* 方法二:getCompletedTaskCount

\* @param args

\* @throws InterruptedException






上述代码处理逻辑还是一样在主线程循环判断,主要就两个方法:
●getTaskCount() :返回计划执行的任务总数。由于任务和线程的状态可能在计算过程中动态变化,返回的值只是一个近似值。这个方法返回的是线程池提交的任务总数,包括已经完成和正在执行中的任务。
●getCompletedTaskCount() :返回已经完成执行的任务的大致总数。由于任务和线程的状态可能在计算过程中动态改变,返回的值只是一个近似值,并且在连续的调用中不会减少。这个方法返回的是已经完成执行的任务数量,不包括正在执行中的任务。

优缺点分析

●优点 :不必关闭线程池,避免了创建和销毁带来的损耗。
●缺点 :使用这种判断存在很大的限制条件;必须确定在循环判断过程中没有新的任务产生。

方法三:CountDownLatch

测试代码

优缺点分析

优点 :代码优雅,不需要对线程池进行操作。 缺点 :需要提前知道线程数量;性能较差;还需要在线程代码块内加上异常判断,否则在 countDown之前发生异常而没有处理,就会导致主线程永远阻塞在 await。

方法四:公共计数

测试代码

这种实现其实就是通过加锁计数,然后循环判断。

优缺点分析

●优点 :手动维护方式更加灵活,对于一些特殊场景可以手动处理。
●缺点 :和CountDownLatch相比,一样需要知道线程数目,但是代码实现比较麻烦。

方法五:Future

测试代码

优缺点分析

优点:使用简单,不需要关闭线程池。
缺点:每个提交给线程池的任务都会关联一个Future对象,这可能会引入额外的内存开销。如果需要处理大量的任务,可能会占用较多的内存。

测试代码汇总