ExecutorService 关闭的问题

问题

最近工作项目里面涉及到了多线程爬虫问题,使用到了线程池去管理线程资源,关于线程池的优点此处不再赘述. 由于之前对于线程池的使用并不是很多,在自己粗略查看Java8 API中有关Executor这一体系(Executor 以及其所有扩展的接口和实现类)的文档后写了一个hello world 程序, 发现了一个有意思的地方. 程序源代码如下:

public class TestExecutor {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(3);
        exec.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    System.out.println("current thread runs");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        System.out.println("main thread over");
    }
}

在idea中的终端端显示运行结果为:

main thread over
current thread runs

Java进程并未关闭, 正常的结果如下:

main thread over
current thread runs

Process finished with exit code 0

分析

这是一个有意思的问题, 再次阅读API中关于ExecutorService这一部分发现问题在于ExecutorService实例并未真正的terminate. ExecutorService实例真正关闭时其实要满足三个条件:1.没有正在执行的task.2.没有等待运行的task. 3.不能够再提交任务到ExecutorService, 于是发现上面的hello world 程序中不满足第三个条件, 翻阅API文档, 有两个方法可以实现关闭ExecutorService. 一个是shutdown 该方法调用后, 表示不可再提交task给ExecutorService, 如果提交将会触发java.util.concurrent.RejectedExecutionException 该方法并不是立刻让运行中的task关闭, 当调用次方法后, 所有task运行完毕后, ExecutorService关闭, Java进程退出. 第二个方法即是shutdownNow 改方法表示立刻终止当前执行的task, 本质上讲,它是触发运行中task线程的中断,并且阻止已经提交尚未执行的task运行. 如果只是为了让如上的hello world Java进程征程结束,那么使用shutdown 即可. 但是在实际开发中, 我们需要考虑是否要终止一些非常耗时的任务的线程运行, 此时就要考虑到对于超过个运行时常的任务线程, 我们需要关闭它, 于是awaitTermination 方法起作用了, 这个方法的调用在shutdown 之后, 这是情理之中, 正常情况下我们要终止ExecutorService的时候肯定是不会在将task提交给它的,这个方法接受一个时间参数且这个方法为以阻塞方法, 它会在设定时间内等待所有任务完成, 如果所有任务完成难么返回true 如果到了设定等待时间还有任务没有完成, 那么返回false 此时我们就可以调用shutdownNow 强制关闭执行耗时任务的线程. 当然在这个方法调用的线程可能触发interrupt, 所以我们也需要考虑在这种情况下的ExecutorService关闭问题, 多说无益, 上代码.

例子

首先创建10个task, 每一个task都有延迟, 延迟时间单元为1s且延迟时间按照时间单元递增. 设定的awaitTermination 等待时间为5s. 因此当把所有的task提交执行的时候会有5个task正常执行完毕, 而另外5个task的线程会因为时间过长而被中断, 代码如下:

public class TestExecutor2 {
    private static long threadWaitUnit = 1000;
    private static long awitTime = 5*threadWaitUnit;
    public static class Mythread implements Runnable{
        private long waitTime;
        public Mythread(long arg){
            this.waitTime = arg;
        }
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName()+" wait time "+waitTime);
                Thread.sleep(waitTime);
            } catch (InterruptedException e) {
                System.out.println("interrupted "+Thread.currentThread().getName());
            }
        }
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(10);
        for(int i=1; i<=10; i++){
            exec.execute(new Mythread(i*threadWaitUnit));
        }
        exec.shutdown();// 不可以再提交执行任务
        try {
            if(!exec.awaitTermination(awitTime, TimeUnit.MILLISECONDS)){
                //规定时间内耗时的task 没有完成
                exec.shutdownNow(); // 强制关闭
            }
        } catch (InterruptedException e) {
            System.out.println("awaitTermination exception "+e.toString());
            exec.shutdownNow();
        }
        System.out.println("main thread over");
    }
}

运行结果:

pool-1-thread-3 wait time 3000
pool-1-thread-7 wait time 7000
pool-1-thread-8 wait time 8000
pool-1-thread-4 wait time 4000
pool-1-thread-1 wait time 1000
pool-1-thread-5 wait time 5000
pool-1-thread-9 wait time 9000
pool-1-thread-6 wait time 6000
pool-1-thread-2 wait time 2000
pool-1-thread-10 wait time 10000
interrupted pool-1-thread-7
interrupted pool-1-thread-8
main thread over
interrupted pool-1-thread-6
interrupted pool-1-thread-9
interrupted pool-1-thread-10

Process finished with exit code 0

分析: 看到标号为6-10的线程耗时是大于5s的, 所以他们被中断了. 以上就是ExecutorService正常关闭的简单处理过程.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值