java ThreadPoolExecutor 关闭方法:shutdown() 、 shutdownNow() 、 awaitTermination() 的用法和区别

说明

ThreadPoolExecutor 提供关闭线程池的方法,有以下3个:

  • shutdown()
  • shutdownNow()
  • awaitTermination() (不能实现关闭)

本文介绍这3个方法的区别

注意

在很多程序中,线程池与数据库连接池类似,即:一直开启并使用,不会关闭

结论

优雅的关闭,用 shutdown()
立刻关闭,并得到未执行任务列表,用 shutdownNow()

shutdown()

将线程池状态置为 SHUTDOWN

不会立即停止,而是做以下操作:

  • 停止接收外部submit的任务
  • 内部正在跑的任务和队列里等待的任务,会执行完
  • 等到第二步完成后,才真正停止

例子

package ThreadPool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Shutdown1 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                1,
                1,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(1)
        );

        threadPoolExecutor.execute(new MyRun("1"));

        threadPoolExecutor.execute(new MyRun("2"));

        // 关闭线程池,内部正在跑的任务和队列里等待的任务,执行完后才会真正关闭
        threadPoolExecutor.shutdown();

        // 关闭后,停止接收新提交的任务,会抛异常
        try {
            threadPoolExecutor.execute(new MyRun("3"));
        }catch (Exception e){
            System.out.println("拒绝接收任务:"+e.toString());
        }
    }
    static class MyRun implements Runnable {
        String name;

        public MyRun(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println(name+"--开始");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(name+"--结束");
        }
    }
}

shutdownNow()

将线程池状态置为 STOP。企图立即停止,事实上不一定

做以下操作:

  • 跟 shutdown() 一样,先停止接收外部提交的任务
  • 忽略队列里等待的任务
  • 尝试将正在跑的任务interrupt中断
  • 返回未执行的任务列表

它试图终止线程的方法是通过调用 Thread.interrupt() 方法来实现的,但是,这种方法的作用有限,如果线程中没有 sleep 、wait、Condition、定时锁等应用, interrupt() 方法是无法中断当前的线程的。

所以,ShutdownNow()并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。

但是 大多数时候是能立即退出的

例子:可以中断线程

通过 Thread.interrupt() 方法 中断线程池中的线程,前提是:线程执行 sleep() 、wait() 等方法

package ThreadPool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Shutdown1 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                1,
                1,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(1)
        );

        threadPoolExecutor.execute(new MyRun("1"));

        threadPoolExecutor.execute(new MyRun("2"));

        // 关闭线程池,内部正在跑的任务和队列里等待的任务,执行完后才会真正关闭
        threadPoolExecutor.shutdown();

        // 关闭后,停止接收新提交的任务,会抛异常
        try {
            threadPoolExecutor.execute(new Shutdown1.MyRun("3"));
        }catch (Exception e){
            System.out.println("拒绝接收任务:"+e.toString());
        }
    }
    static class MyRun implements Runnable {
        String name;

        public MyRun(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println(name+"--开始");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(name+"--结束");
        }
    }
}

例子:不能中断线程

由于线程中执行死循环,没有执行 sleep() 等操作,所以无法中断

package ThreadPool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Shutdown3 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                1,
                1,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(1)
        );

        threadPoolExecutor.execute(new MyRun("1"));

        /*
         关闭线程池
         通过 Thread.interrupt() 方法 中断线程池中的线程
         由于线程执行 死循环,所以无法中断
         */
        threadPoolExecutor.shutdownNow();

        // 关闭后,停止接收新提交的任务,会抛异常
        try {
            threadPoolExecutor.execute(new MyRun("2"));
        }catch (Exception e){
            System.out.println("拒绝接收任务:"+e.toString());
        }
    }
    static class MyRun implements Runnable {
        String name;

        public MyRun(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println(name+"--开始");
            for(int i=0;i<10000000;++i){
                int res = i % 10000;
                if(res == 0) {
                    System.out.println(name + "--运行中,循环次数:" +i);
                }
            }
        }
    }
}

shutdown()和shutdownNow()的区别

shutdownNow() 能立即停止线程池,正在跑的和正在等待的任务都停下了。这样做立即生效,但是风险也比较大;

shutdown() 只是关闭了提交通道,而内部线程还正常运行,运行完再停止线程池。

awaitTermination()

不是用来关闭线程池,而是等待 timeout 时间后,返回线程池 是否关闭

应用场景:一般在调用 shutdown() 方法后调用

方法声明

public boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException

形参:

  • timeout:时间
  • unit:时间单位(当前是秒)

返回值: boolean 型,返回以下值:

  • true:表示已经停止
  • false:表示正在运行

执行后,当前线程阻塞(等待),直到满足下面条件之一后才会继续执行:

  • 执行 shutdown() 后,线程运行时间超过 timeout 时间,等待 timeout 时间返回 false

  • 执行 shutdown() 后,线程运行时间少于 timeout 时间,等待 timeout 时间返回 true

  • 执行 shutdownNow() 后,抛出InterruptedException 异常,返回true

例子1

执行 shutdown() 后,由于线程需要运行 3秒 结束,awaitTermination() 只等待 1秒 就返回线程池状态,所以返回false

package ThreadPool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Shutdown4 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                2,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(1)
        );

        threadPoolExecutor.execute(new MyRun("1"));

        threadPoolExecutor.execute(new MyRun("2"));


        try {
            threadPoolExecutor.shutdown();
            // 由于线程需要运行3秒结束,这里只等待1秒就返回线程池状态,所以返回false
            boolean res = threadPoolExecutor.awaitTermination(1,TimeUnit.SECONDS);
            System.out.println(res);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    static class MyRun implements Runnable {
        String name;

        public MyRun(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println(name+"--开始");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Thread.interrupted();
            System.out.println(name+"--结束");
        }
    }
}

例子2

执行 shutdown() 后,线程需要运行 3秒 结束, awaitTermination() 等待5秒返回线程池状态,所以返回true

package ThreadPool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Shutdown5 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                2,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(1)
        );

        threadPoolExecutor.execute(new MyRun("1"));

        threadPoolExecutor.execute(new MyRun("2"));


        try {
            threadPoolExecutor.shutdown();
            // 线程需要运行3秒结束,这里等待5秒返回线程池状态,所以返回true
            boolean res = threadPoolExecutor.awaitTermination(5,TimeUnit.SECONDS);
            System.out.println(res);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    static class MyRun implements Runnable {
        String name;

        public MyRun(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println(name+"--开始");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Thread.interrupted();
            System.out.println(name+"--结束");
        }
    }
}

例子3

执行 shutdownNow() 后,抛出 java.lang.InterruptedException 异常,awaitTermination() 立即返回true

package ThreadPool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Shutdown6 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                2,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(1)
        );

        threadPoolExecutor.execute(new MyRun("1"));

        threadPoolExecutor.execute(new MyRun("2"));


        try {
            // 执行 shutdownNow() 后,抛出 java.lang.InterruptedException 异常,awaitTermination() 立即返回true
            threadPoolExecutor.shutdownNow();
            boolean res = threadPoolExecutor.awaitTermination(5,TimeUnit.SECONDS);
            System.out.println(res);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    static class MyRun implements Runnable {
        String name;

        public MyRun(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println(name+"--开始");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Thread.interrupted();
            System.out.println(name+"--结束");
        }
    }
}

参考:
https://blog.csdn.net/u012168222/article/details/52790400
https://blog.csdn.net/thetimelyrain/article/details/106902289
https://juejin.cn/post/6996665200711565319


原文出处:/show_1IX4che6kkdN.html