如果优雅地关闭ExecutorService提供的java线程池

ExecutorService让我们可以优雅地在程序中使用线程池来创建和管理线程,而且性能佳、开销小,还可以有效地控制最大并发线程数,是我们在java并发编程中会经常使用到的。

每一个线程都会占用系统资源,因此线程池的关闭与清理同样重要,本文介绍我们如何优雅地关闭线程池。

一. ExecutorService中关闭线程池的方法

1. shutdown()

停止接收新任务,原来的任务继续执行

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

2. shutdownNow()

停止接收新任务,原来的任务停止执行

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

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

3. awaitTermination(long timeOut, TimeUnit unit)

当前线程阻塞,timeout 和 TimeUnit 两个参数,用于设定超时的时间及单位,当前线程阻塞,直到:

  • 等所有已提交的任务(包括正在跑的和队列中等待的)执行完;
  • 或者 等超时时间到了(timeout 和 TimeUnit设定的时间);
  • 或者 线程被中断,抛出InterruptedException

然后会监测 ExecutorService 是否已经关闭,返回true(shutdown请求后所有任务执行完毕)或false(已超时)

二. 三种方法的区别

1. shutdown() 和 shutdownNow() 的区别

  • shutdown()?只是关闭了提交通道,用submit()是无效的;而内部该怎么跑还是怎么跑,跑完再停。
  • shutdownNow()?能立即停止线程池,正在跑的和正在等待的任务都停下了。

2. shutdown() 和 awaitTermination() 的区别

  • shutdown()?后,不能再提交新的任务进去;但是?awaitTermination()?后,可以继续提交。
  • awaitTermination() 是阻塞的,返回结果是线程池是否已停止(true/false);shutdown()?不阻塞。

3. 总结

  1. 优雅的关闭,用 shutdown()
  2. 想立马关闭,并得到未执行任务列表,用shutdownNow()
  3. 优雅的关闭,并允许关闭声明后新任务能提交,用 awaitTermination()
  4. 关闭功能 【从强到弱】 依次是:shuntdownNow() > shutdown() > awaitTermination()

三. RunTime.getRunTime().addShutdownHook()的作用

RunTime.getRunTime().addShutdownHook()的作用就是在JVM销毁前执行的最后一个线程,通过addShutdownHook添加钩子,当系统执行完这些钩子后,jvm才会关闭,因此我们可以在这个线程中把我们前面使用ExecutorService创建的线程池优雅地关闭掉。
在web3j中异步执行类(Async)中有如下代码:

// 创建线程池
private static final ExecutorService executor = Executors.newCachedThreadPool();

// 添加关闭线程池的钩子
static {
    Runtime.getRuntime().addShutdownHook(new Thread(() - > shutdown(executor)));
}

// 关闭线程池的钩子函数
private static void shutdown(ExecutorService executorService) {
    // 第一步:使新任务无法提交
    executorService.shutdown();
    try {
        // 第二步:等待未完成任务结束
        if(!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
             // 第三步:取消当前执行的任务
            executorService.shutdownNow();
            // 第四步:等待任务取消的响应
            if(!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                System.err.println("Thread pool did not terminate");
            }
        }
    } catch(InterruptedException ie) {
        // 第五步:出现异常后,重新取消当前执行的任务
        executorService.shutdownNow();
        Thread.currentThread().interrupt(); // 设置本线程中断状态
    }
}

原文地址:https://blog.51cto.com/634435/2476232

时间: 2024-12-13 22:39:39

如果优雅地关闭ExecutorService提供的java线程池的相关文章

转Java线程池ExecutorService

开篇前,我们先来看看不使用线程池的情况: new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? 1 2 3 4 5 6 7 new Thread(new Runnable() {     @Override     public void run() {         // TODO Auto-generated method stub     } }).start(); 那你就太out了,new Thread的弊端如下:a. 每次new Thread新建对象性能差

Java线程池 ExecutorService了解一下

本篇主要涉及到的是java.util.concurrent包中的ExecutorService.ExecutorService就是Java中对线程池的实现. 一.ExecutorService介绍 ExecutorService是Java中对线程池定义的一个接口,它java.util.concurrent包中,在这个接口中定义了和后台任务执行相关的方法:  Java API对ExecutorService接口的实现有两个,所以这两个即是Java线程池具体实现类: 1. ThreadPoolExe

Java线程池:ExecutorService,Executors

简单的Java线程池可以从Executors.newFixedThreadPool( int n)获得.此方法返回一个线程容量为n的线程池.然后ExecutorService的execute执行之. 现给出一个示例. package zhangphil.executorservice; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ZhangPhil

java线程池学习(一)

前言 在实际工作中,线程是一个我们经常要打交道的角色,它可以帮我们灵活利用资源,提升程序运行效率.但是我们今天不是探讨线程!我们今天来聊聊另一个与线程息息相关的角色:线程池.本篇文章的目的就是全方位的解析线程池的作用,以及jdk中的接口,实现以及原理,另外对于某些重要概念,将从源码的角度探讨. tip:本文较长,建议先码后看. 线程池介绍 首先我们看一段创建线程并且运行的常用代码: for (int i = 0; i < 100; i++) { new Thread(() -> { Syste

Java线程池使用说明

Java线程池使用说明 一 简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用.为我们在开发中处理线程的问题提供了非常大的帮助. 二:线程池 线程池的作用: 线程池作用就是限制系统中执行线程的数量.     根 据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少

java线程池分析和应用

比较 在前面的一些文章里,我们已经讨论了手工创建和管理线程.在实际应用中我们有的时候也会经常听到线程池这个概念.在这里,我们可以先针对手工创建管理线程和通过线程池来管理做一个比较.通常,我们如果手工创建线程,需要定义线程执行对象,它实现的接口.然后再创建一个线程对象,将我们定义好的对象执行部分装载到线程中.对于线程的创建.结束和结果的获取都需要我们来考虑.如果我们需要用到很多的线程时,对线程的管理就会变得比较困难.我们手工定义线程的方式在时间和空间效率方面会存在着一些不足.比如说我们定义好的线程

Java线程池介绍

Java线程池介绍 2015-10-24 ImportNew (点击上方公号,可快速关注) 原文:allegro 译文:ImportNew - paddx 链接:http://www.importnew.com/16845.html 根据摩尔定律(Moore’s law),集成电路晶体管的数量差不多每两年就会翻一倍.但是晶体管数量指数级的增长不一定会导致 CPU 性能的指数级增长.处理器制造商花了很多年来提高时钟频率和指令并行.在新一代的处理器上,单线程程序的执行速率确实有所提高.但是,时钟频率

Java线程池的几种实现 及 常见问题讲解

工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能.所以,“池”的用处就凸显出来了. 1. 为什么要使用线程池 在3.6.1节介绍的实现方式中,对每个客户都分配一个新的工作线程.当工作线程与客户通信结束,这个线程就被销毁.这种实现方式有以下不足之处: 服务器创建和销毁工作的开销( 包括所花费的时间和系统资源 )很大.这一项不用解释,可以去查下"线程

JAVA线程池简介

一 简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用.为我们在开发中处理线程的问题提供了非常大的帮助. 二 线程池线程池的作用: 线程池作用就是限制系统中执行线程的数量.根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率