线程池关闭流程

线程池的状态转换:在这里插入图片描述

在这里插入图片描述

关闭线程池的方法
  • shutdown():把线程池状态修改为SHUTDOWN,并通过interruptIdleWorkers()中断所有空闲工作者线程。
  • shutdownNow():把线程池状态修改为STOP,移除队列中的任务并通过interruptWorkers()中断所有工作者线程。

  shutdownNow()会中断正在工作的工作者线程,也仅仅是中断目标线程,但目标线程不一定响应中断或者已处理中断异常,它并不是停止目标线程。

线程池是如何判断工作者线程是空闲还是忙碌呢?

  工作者线程Worker是继承AbstractQueuedSynchronizerd的,锁被占用时线程就是忙碌状态,锁空闲时线程就是空闲状态。

为什么要中断工作者线程?

  中断空闲的工作者线程:因为线程可能在getTask()中的take()或者poll()方法处阻塞,通过中断就能够唤醒这些被阻塞的线程,然后这些被唤醒线程如果获取不到任务的话,就会被回收。
  中断忙碌的工作者线程:主要是为了取消任务的执行,但不一定成功,因为任务不一定响应中断或则处理了中断异常。

线程池关闭的流程

  在执行任何可能终止线程池的操作都会调用tryTerminate()去尝试关闭线程池,比如:

  • processWorkerExit():工作者线程退出
  • remove():移除队列中的某个任务
  • purge():移除队列中已取消的任务
  • shutdown():拒绝提交任务
  • shutdownNow():拒绝提交任务并移除队列中的任务
tryTerminate()源码解析:
final void tryTerminate() {
        for (;;) {
        	//线程池状态
            int c = ctl.get();
            // 线程池是运行状态
            if (isRunning(c) ||
            	// 线程池是TIDYING或者TERMINATED状态
                runStateAtLeast(c, TIDYING) ||
                // 线程池是SHUTDOWN但是队列中还有任务
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            if (workerCountOf(c) != 0) { // Eligible to terminate
            	// 线程池达到终止的条件,但队列中还有任务,中断所有空闲的线程。
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
            	//设置线程池的状态为TIDYING
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                    	//默认的实现为一个空方法,用户可以重写它!
                        terminated();
                    } finally {
                    	// 设置线程池的状态为TERMINATED
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

总结

  • 线程池的状态为SHUTDOWN或STOP任务队列为空工作者线程数为0时,线程池就会进入TIDYING状态,当执行完terminated()方法后,就会进入最终的TERMINATED状态。
  • 如果任务是一个死循环且不响应中断的话,那么这个线程池永远也不会被关闭。
### 正确使用线程池及其完整操作流程 #### 1. 创建线程池 为了正确使用线程池,应避免直接通过 `Executors` 工厂类创建线程池,而推荐使用 `ThreadPoolExecutor` 构造函数手动配置线程池参数。这种方式可以让开发者更清楚地理解线程池的工作机制,并有效控制资源消耗[^2]。 以下是基于 `ThreadPoolExecutor` 的线程池创建代码示例: ```java import java.util.concurrent.*; public class ThreadPoolExample { public static void main(String[] args) { int corePoolSize = 5; // 核心线程数 int maximumPoolSize = 10; // 最大线程数 long keepAliveTime = 1L; // 空闲线程存活时间 TimeUnit unit = TimeUnit.MINUTES; // 时间单位 BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10); // 队列大小 ThreadFactory threadFactory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "Custom-Thread-Pool-" + System.nanoTime()); } }; RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略 ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler ); // 提交任务到线程池 for (int i = 0; i < 15; i++) { final int taskNumber = i; executor.submit(() -> { System.out.println("Task " + taskNumber + " is running on thread " + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } }); } // 关闭线程池 executor.shutdown(); } } ``` 上述代码展示了如何自定义核心线程数、最大线程数、队列长度以及拒绝策略等重要参数[^4]。 --- #### 2. 提交任务至线程池 提交任务可以通过两种主要方法实现:`execute()` 和 `submit()`。 - **`execute()` 方法**:仅支持无返回值的任务(即实现了 `Runnable` 接口的对象)。 - **`submit()` 方法**:不仅支持无返回值的任务,还支持有返回值的任务(即实现了 `Callable` 接口的对象),并通过 `Future` 对象获取任务执行结果。 --- #### 3. 获取活动线程数量 如果需要监控线程池的状态,可以调用 `getActiveCount()` 方法来获取当前正在运行的线程数目[^1]。 --- #### 4. 关闭线程池 当不再需要线程池时,应当及时关闭以释放资源。通常有两种关闭方式: - **`shutdown()` 方法**:平滑停止线程池,已提交的任务将继续执行完毕,但不会接受新任务。 - **`shutdownNow()` 方法**:立即尝试终止所有正在执行的任务,并返回尚未开始执行的任务列表。 --- #### 完整操作流程总结 1. 使用 `ThreadPoolExecutor` 手动配置线程池的核心参数。 2. 将任务提交给线程池,可以选择同步或异步的方式。 3. 如果需要动态调整线程池状态,可查询其内部指标(如活跃线程数)。 4. 当线程池使命完成后,务必调用 `shutdown()` 或 `shutdownNow()` 进行清理。 --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值