显式关闭线程池的正确姿势

一、shutdown

提到 Java 线程池的关闭,绝大多数人第一时间想到的是 ExecutorServiceshutdown 方法。

关于 shutdown 方法,注释上是这么说明的。

/**
 * Initiates an orderly shutdown in which previously submitted
 * tasks are executed, but no new tasks will be accepted.
 * Invocation has no additional effect if already shut down.
 */

大概意思是,当执行了 shutdown 方法后,线程池将等待已提交任务执行完成后关闭,但在此期间不再接收新的任务。

对此,我们编写一段代码来测试 shutdown 的效果。(注意:这段代码是不完整的,具体原因见下文)

package com.bolt.panda;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.*;

/**
 * Created by panda on 2019/2/13.
 */
public class ShutdownDemo {

    public static void main(String[] args) {
        System.out.println("主线程运行开始!");

        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("pool-%d").build();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), threadFactory);

        executor.submit(new Task());
        executor.submit(new Task());
        executor.submit(new Task());

        executor.shutdown();

        System.out.println("主线程运行结束!");
    }
}

class Task implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println("子线程执行中...");
        return null;
    }

}

输出结果为:

主线程运行开始!
子线程执行中...
主线程运行结束!
子线程执行中...
子线程执行中...

Process finished with exit code 0

可见,线程池确实是关闭了,因为程序退出了。这里需要解释一下,若线程池没有关闭,则会一直等待任务,不会出现程序退出的现象(因为我们设置的核心线程数大于 0)。

但是,在这里我们发现了一个问题,主线程先于子线程退出了。这一点在很多情况下是不合理的,因为我们常常需要在多线程任务执行结束后回到主线程处理其他工作,例如统计数据等。那么,有什么办法能实现在线程池关闭前主线程保持阻塞状态么?这时候就需要借助 awaitTermination 方法了。

二、awaitTermination

我们先来看看 awaitTermination 的说明:

/**
 * Blocks until all tasks have completed execution after a shutdown
 * request, or the timeout occurs, or the current thread is
 * interrupted, whichever happens first.
 *
 * @param timeout the maximum time to wait
 * @param unit the time unit of the timeout argument
 * @return {@code true} if this executor terminated and
 *         {@code false} if the timeout elapsed before termination
 * @throws InterruptedException if interrupted while waiting
 */
boolean awaitTermination(long timeout, TimeUnit unit)
    throws InterruptedException;

大体意思是,在主线程调用 shutdown 方法后,若此时再调用 awaitTermination 方法,则主线程会保持阻塞状态,除非发生了以下 3 种情况:

  • 线程池中的所有任务已执行完毕
  • 阻塞时长超过设置的最大阻塞时间
  • 发生了中断异常

此外,只有当检测到线程池所有任务执行完毕时,awaitTermination 方法才会返回 true,否则返回 false

基于以上,我们可以结合 awaitTermination 实现线程池先于主线程关闭。现在,让我们对之前的代码稍作补充,具体如下:

package com.bolt.panda;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.*;

/**
 * Created by panda on 2019/2/13.
 */
public class ShutdownDemo {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程运行开始!");

        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("pool-%d").build();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), threadFactory);

        executor.submit(new Task());
        executor.submit(new Task());
        executor.submit(new Task());

        executor.shutdown();

        while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
            System.out.println("检测到仍有活跃子线程,保持阻塞状态...");
        }

        System.out.println("主线程运行结束!");
    }
}

class Task implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println("子线程执行中...");
        return null;
    }

}

输出结果为:

主线程运行开始!
子线程执行中...
子线程执行中...
子线程执行中...
主线程运行结束!

Process finished with exit code 0

很明显地看到,主线程不再先于子线程退出,而是在线程池关闭后,再完成最后的收尾工作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值