Java并发编程一如何关闭线程池

Java并发编程一如何关闭线程池

当线程池中已经有大量线程正在处理任务,并且任务队列中也有很多任务正在等待被处理,这个时候我们该如何去关闭线程池呢?线程池的后续处理又是怎么的呢?

首先我们来创建一个任务。

任务

ShutDownTask类实现了Runnable接口,它的run()就是我们线程所要执行的任务,我们这里的任务非常简单,就是输出自己(当前线程)的名称,再sleep(500)即可。

class ShutDownTask implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(500);
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "被中断了");
        }
    }
}

shutdown()

我们先来测试一下shutdown()的作用。
测试代码:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1000);
        executorService.shutdown();
    }
}

输出

pool-1-thread-6
pool-1-thread-5
pool-1-thread-7
pool-1-thread-9
pool-1-thread-10
pool-1-thread-4
pool-1-thread-2
pool-1-thread-8
pool-1-thread-1
pool-1-thread-3
pool-1-thread-5
pool-1-thread-6
pool-1-thread-8
pool-1-thread-10
pool-1-thread-2

我只粘贴了一部分输出,其实会输出100行,也就是线程池会处理完所有的任务,既包括正在执行的任务,还包括已经提交到任务队列上的任务,我们从代码也可以分析出来,每个任务会sleep(500),而在main线程中提交任务后,只sleep(1000),而main线程sleep(1000)后,很显然所有的任务是不可能已经被全部执行完的,而我们这里定义的线程池是FixedThreadPool,它最多只能存在10个线程去执行任务,所以肯定有任务在任务队列中,而当main线程调用线程池的shutdown()后,所有任务依然全部执行完了,可以说明shutdown()会处理完所有的任务,既包括正在执行的任务,还包括已经提交到任务队列上的任务。

可以得出,调用shutdown()后线程池的后续操作:

  1. 停止接收新submit的任务。
  2. 已经提交的任务(包括正在执行的任务和队列中等待的任务),都会继续执行完成。
  3. 等到第2步完成后,才真正停止。

shutdown()后线程池是否还能提交任务?

测试代码:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1000);

        executorService.shutdown();
        executorService.execute(new ShutDownTask());
    }
}

在这里插入图片描述
很明显是不能再提交任务的,线程池拒绝了提交的任务,之前说过,当线程池满了(不能再创建线程去处理任务,即线程数为最大线程数maximumPoolSize,任务队列也装满了任务)之后也会拒绝提交的任务,这里又多了一种情况。

如果我们就想让线程池现在、马上、立刻就停止呢?接着看。

shutdownNow()

测试代码:

package threadpool;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1000);
        List<Runnable> runnableList = executorService.shutdownNow();
    }
}

输出:

pool-1-thread-8
pool-1-thread-4
pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
pool-1-thread-6
pool-1-thread-7
pool-1-thread-9
pool-1-thread-10
pool-1-thread-5
pool-1-thread-3
pool-1-thread-1
pool-1-thread-2
pool-1-thread-4
pool-1-thread-8
pool-1-thread-5
pool-1-thread-6
pool-1-thread-10
pool-1-thread-7
pool-1-thread-9
pool-1-thread-2被中断了
pool-1-thread-5被中断了
pool-1-thread-3被中断了
pool-1-thread-6被中断了
pool-1-thread-4被中断了
pool-1-thread-8被中断了
pool-1-thread-1被中断了

这是全部输出了,很显然正在执行的任务会被中断执行,在任务队列中的线程会被直接忽略。

Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.

翻译:尝试停止所有正在执行的任务,停止处理等待的任务,并返回等待执行任务的列表。
上面代码中的runnableList 就是等待执行任务的列表,以便程序可以做后续处理,如重新创建线程池去处理等。

调用此方法当然也会拒绝新提交的任务,大家可以自己去试一试,要多动手敲代码,代码才会认识你。

可以得出,调用shutdownNow()后线程池的后续操作:

  1. shutdown()一样,先停止接收新submit的任务。
  2. 忽略任务队列里等待的任务。
  3. 尝试将正在执行的任务interrupt中断。
  4. 返回未执行的任务列表。

线程池试图终止线程的方法是通过调用interrupt()方法来实现的,这种方法的作用有限,如果线程中没有sleepwaitCondition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,shutdownNow()并不代表线程池就一定立即能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。但是大多数时候是能立即退出的。

awaitTermination()

测试代码:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1500);

        executorService.shutdown();
        boolean b = executorService.awaitTermination(1L, TimeUnit.SECONDS);
        System.out.println(b);
    }
}

结果:
在这里插入图片描述
测试代码:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1500);

        executorService.shutdown();
        boolean b = executorService.awaitTermination(7L, TimeUnit.SECONDS);
        System.out.println(b);
    }
}

结果:
在这里插入图片描述
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
     */

根据测试结果与注释我们得出,调用此方法main线程会阻塞,直到:

  • 所有已提交的任务(包括正在执行的任务和队列中等待的任务)执行完。
  • 超时时间到了(timeoutTimeUnit设定的时间)。
  • 线程被中断,抛出InterruptedException
发布了309 篇原创文章 · 获赞 448 · 访问量 71万+

猜你喜欢

转载自blog.csdn.net/qq_37960603/article/details/104338929