1、创建线程池
使用Executors工厂类或者ThreadPoolExecutor的构造函数来创建线程池。通常,推荐直接使用ThreadPoolExecutor构造函数来明确指定线程池的参数,如核心线程数、最大线程数、空闲线程存活时间、工作队列等。
2、执行任务
通过调用线程池的submit或execute方法来提交任务。这些任务将排队等待执行,直到线程池中的线程变得可用。
3、监控线程池状态
线程池的状态可以通过ThreadPoolExecutor的一些方法进行检查,如getActiveCount(当前活跃线程数)、getPoolSize(当前线程池大小)、getQueue(工作队列)等。此外,可以使用shutdownNow或shutdown方法来获取当前正在等待执行的任务列表。
4、优雅地关闭线程池
线程池应该被优雅地关闭,以确保所有正在执行的任务都能完成,并且不再接受新的任务。这可以通过调用shutdown或shutdownNow方法来实现。
shutdown():启动线程池的关闭序列。已提交的任务将被执行,但不会接受新的任务。如果已经关闭,则调用不执行任何操作。
shutdownNow():试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
在调用shutdown或shutdownNow之后,应该调用awaitTermination方法来等待线程池中的所有任务都执行完毕。这个方法会阻塞,直到所有任务都执行完毕,或者超时时间到达。
5、处理未完成任务
如果线程池在关闭时还有未完成的任务,可以通过shutdownNow方法返回的列表来处理这些任务。这些任务可以被重新提交到另一个线程池,或者进行其他处理。
6、资源清理
在关闭线程池后,不再需要对其进行引用,可以将其设置为null以便垃圾回收器回收其占用的内存。但是,请注意,在调用shutdown或shutdownNow之后,线程池实例本身并不会立即被垃圾回收,因为它可能还有正在执行的任务或者等待执行的任务的引用。因此,将线程池实例设置为null只是为了断开对它的引用,而不是立即释放其占用的内存。
7、简单的示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class ThreadPoolLifecycleExample {public static void main(String[] args) throws InterruptedException {// 1. 创建线程池ExecutorService executorService = Executors.newFixedThreadPool(5);// 2. 提交任务for (int i = 0; i < 10; i++) {final int taskId = i;executorService.submit(() -> {// 模拟任务执行try {TimeUnit.SECONDS.sleep(taskId);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("Task " + taskId + " completed.");});}// 3. 优雅地关闭线程池executorService.shutdown(); // 不再接受新任务,但会等待已提交的任务完成// 等待线程池中的所有任务都执行完毕(可选)try {if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {// 超时了,可以考虑取消任务或者做其他处理executorService.shutdownNow(); // 尝试停止所有正在执行的任务}} catch (InterruptedException ie) {// 如果当前线程在等待过程中被中断,则重新中断executorServiceexecutorService.shutdownNow();// 保留中断状态以便进一步处理Thread.currentThread().interrupt();}// 4. 资源清理(可选)executorService = null;}
}