java中线程池最实用的创建与关闭指南

目录
  • 前言
  • 线程池创建
  • 只需要执行shutdown就可以优雅关闭
  • 执行shutdownNow关闭的测试
  • 总结

前言

在日常的开发工作当中,线程池往往承载着一个应用中最重要的业务逻辑,因此我们有必要更多地去关注线程池的执行情况,包括异常的处理和分析等。

线程池创建

避免使用Executors创建线程池,主要是避免使用其中的默认实现,那么我们可以自己直接调用ThreadPoolExecutor的构造函数来自己创建线程池。在创建的同时,给BlockQueue指定容量就可以了。

private static ExecutorService executor = new ThreadPoolExecutor(10, 10,
        60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue(10));

这种情况下,一旦提交的线程数超过当前可用线程数时,就会抛出java.util.concurrent.RejectedExecutionException,这是因为当前线程池使用的队列是有边界队列,队列已经满了便无法继续处理新的请求。但是异常(Exception)总比发生错误(Error)要好。

除了自己定义ThreadPoolExecutor外。还有其他方法。这个时候第一时间就应该想到开源类库,如apache和guava等。

推荐使用guava提供的ThreadFactoryBuilder来创建线程池。

public class ExecutorsDemo {

    private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("demo-pool-%d").build();

    private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {

        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            pool.execute(new SubThread());
        }
    }
}

只需要执行shutdown就可以优雅关闭

package com.zxd.concurrent;

import com.google.common.collect.Lists;

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

public class ThreadPool {

    public static void main(String[] args) {
        // TODO 如何正确优雅简单的关闭线程池 ,无须其他多余操 ;创建线程池我选择用这种方法比较可靠
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        //接收处理数据的结果集合
        List<Integer> resList = Lists.newArrayList();
        //开启的任务 我们设置的核心处理线程数5个所以 会有多出来的线程在队列中等待
        for (int i = 0; i < 30; i++) {
            executor.execute(new Task(i, resList));
        }
        //1、关闭线程池 一定要在循环结束关闭
        //2、这个关闭方法不会立即关闭所有在执行的任务线程,
        executor.shutdown();
        //4.这里是检查线程池是否所有任务都执行完毕关闭
        int j = 0;
        while (true) {
            //5.这里是等线程池彻底关闭以后做的判断 保证所有线程池已经全部关闭退出while循环
            if (executor.isTerminated()) {
                System.out.println("所有线程已经运行完毕:" + j);
                break;
            }
            // 为避免一直循环 加个睡眠
            try {
                //如果执行shutdown方法没有关闭的线程池线程池会尝试关闭
                System.out.println("尝试关闭线程次数:" + j);
                j++;
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //FIXME 3、下面的方法会立即关闭线程池,没有执行完的也不会在执行了,如果有等待队列的任务也不会继续执行
        System.out.println("【完成的总线程数】:" + resList.size());

    }

    static class Task implements Runnable {
        int name;

        List<Integer> list;

        public Task(int name, List<Integer> list) {
            this.name = name;
            this.list = list;
        }

        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                int j = i * 10;
                // 做业务处理
                //System.out.println("task " + name + " is running");
            }
            list.add(name + 1);
            System.out.println("task " + name + " is over");
        }
    }

}

输出结果

task 0 is over
task 3 is over
task 1 is over
task 2 is over
task 7 is over
task 6 is over
task 5 is over
尝试关闭线程次数:0
task 10 is over
task 9 is over
task 4 is over
task 8 is over
task 14 is over
task 13 is over
task 12 is over
task 11 is over
task 18 is over
task 17 is over
task 16 is over
task 15 is over
task 22 is over
task 21 is over
task 20 is over
task 19 is over
task 26 is over
task 25 is over
task 24 is over
task 23 is over
task 29 is over
task 28 is over
task 27 is over
所有线程已经运行完毕:1
【完成的总线程数】:30

执行shutdownNow关闭的测试

package com.zxd.concurrent;

import com.google.common.collect.Lists;

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

public class ThreadPool {

    public static void main(String[] args) {
        // TODO 如何正确优雅简单的关闭线程池 ,无须其他多余操 ;创建线程池我选择用这种方法比较可靠
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        //接收处理数据的结果集合
        List<Integer> resList = Lists.newArrayList();
        //开启的任务 我们设置的核心处理线程数5个所以 会有多出来的线程在队列中等待
        for (int i = 0; i < 200; i++) {
            executor.execute(new Task(i, resList));
        }
        //1、关闭线程池 一定要在循环结束关闭
        //2、这个关闭方法不会立即关闭所有在执行的任务线程,
//        executor.shutdown();
        //FIXME 3、下面的方法会立即关闭线程池,没有执行完的也不会在执行了,如果有等待队列的任务也不会继续执行
        List<Runnable> list = executor.shutdownNow();
        System.out.println("c剩余的没有执行的任务【线程数】= " + list.size());
        System.out.println("【完成的总线程数】:" + resList.size());
        //4.这里是检查线程池是否所有任务都执行完毕关闭
        int j = 0;
        while (true) {
            //5.这里是等线程池彻底关闭以后做的判断 保证所有线程池已经全部关闭退出while循环
            if (executor.isTerminated()) {
                System.out.println("所有线程已经运行完毕:" + j);
                break;
            }
            // 为避免一直循环 加个睡眠
            try {
                //如果执行shutdown方法没有关闭的线程池线程池会尝试关闭
                System.out.println("尝试关闭线程次数:" + j);
                j++;
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    static class Task implements Runnable {
        int name;

        List<Integer> list;

        public Task(int name, List<Integer> list) {
            this.name = name;
            this.list = list;
        }

        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                int j = i * 10;
                // 做业务处理
                //System.out.println("task " + name + " is running");
            }
            list.add(name + 1);
            System.out.println("task " + name + " is over");
        }
    }

}

输出结果

总结

到此这篇关于java中线程池最实用的创建与关闭的文章就介绍到这了,更多相关java线程池创建与关闭内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-09-09

Java多线程导致CPU占用100%解决及线程池正确关闭方式

简介 情景:1000万表数据导入内存数据库,按分页大小10000查询,多线程,15条线程跑. 使用了ExecutorService executor = Executors.newFixedThreadPool(15) 本地跑了一段时间后,发现电脑CPU逐渐升高,最后CPU占用100%卡死,内存使用也高达80%. 排查问题 Debug 发现虽然创建了定长15的线程池,但是因为数据量大,在For中循环分页查询的List会持续加入LinkedBlockingQueue() 队列中每一个等待的任务,又

很多人竟然不知道Java线程池的创建方式有7种

目录 前言 什么是线程池? 线程池使用 1.FixedThreadPool 2.CachedThreadPool 3.SingleThreadExecutor 4.ScheduledThreadPool 5.SingleThreadScheduledExecutor 6.newWorkStealingPool 7.ThreadPoolExecutor 线程池的执行流程 线程拒绝策略 自定义拒绝策略 究竟选用哪种线程池? 前言 根据摩尔定律所说:集成电路上可容纳的晶体管数量每 18 个月翻一番,因

java线程池使用后到底要关闭吗

线程池做什么 网络请求通常有两种形式: 第一种,请求不是很频繁,而且每次连接后会保持相当一段时间来读数据或者写数据,最后断开,如文件下载,网络流媒体等. 另一种形式是请求频繁,但是连接上以后读/写很少量的数据就断开连接.考虑到服务的并发问题,如果每个请求来到以后服务都为它启动一个线程,那么这对服务的资源可能会造成很大的浪费,特别是第二种情况. 因为通常情况下,创建线程是需要一定的耗时的,设这个时间为T1,而连接后读/写服务的时间为T2,当T1>>T2时,我们就应当考虑一种策略或者机制来控制,使

java 优雅关闭线程池的方案

我们经常在项目中使用的线程池,但是是否关心过线程池的关闭呢,可能很多时候直接再项目中直接创建线程池让它一直运行当任务执行结束不在需要了也不去关闭,这其实是存在非常大的风险的,大量的线程常驻在后台对系统资源的占用是巨大的 ,甚至引发异常.所以在我们平时使用线程池时需要注意优雅的关闭,这样可以保证资源的管控. 在 Java 中和关闭线程池相关的方法主要有如下: void shutdown() List<Runnable> shutDownNow boolean awaitTermination b

Java 线程池详解及创建简单实例

Java 线程池 最近在改进项目的并发功能,但开发起来磕磕碰碰的.看了好多资料,总算加深了认识.于是打算配合查看源代码,总结并发编程的原理. 准备从用得最多的线程池开始,围绕创建.执行.关闭认识线程池整个生命周期的实现原理.后续再研究原子变量.并发容器.阻塞队列.同步工具.锁等等主题.java.util.concurrent里的并发工具用起来不难,但不能仅仅会用,我们要read the fucking source code,哈哈.顺便说声,我用的JDK是1.8. Executor框架 Exec

JAVA 创建线程池的注意事项

1.创建线程或线程池时请指定有意义的线程名称,方便出错时回溯.创建线程池的时候请使用带ThreadFactory的构造函数,并且提供自定义ThreadFactory实现或者使用第三方实现. ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() .setNameFormat("demo-pool-%d").build(); ExecutorService singleThreadPool = new ThreadPoo

了解Java线程池创建过程

前言 最近在改进项目的并发功能,但开发起来磕磕碰碰的.看了好多资料,总算加深了认识.于是打算配合查看源代码,总结并发编程的原理. 准备从用得最多的线程池开始,围绕创建.执行.关闭认识线程池整个生命周期的实现原理.后续再研究原子变量.并发容器.阻塞队列.同步工具.锁等等主题.java.util.concurrent里的并发工具用起来不难,但不能仅仅会用,我们要read the fucking source code,哈哈.顺便说声,我用的JDK是1.8. Executor框架 Executor是一

详解Java线程池的增长过程

通过ThreadPoolExecutor的方式创建线程池 ThreadPoolExecutor 构造方法: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handle

详解Java线程池和Executor原理的分析

详解Java线程池和Executor原理的分析 线程池作用与基本知识 在开始之前,我们先来讨论下"线程池"这个概念."线程池",顾名思义就是一个线程缓存.它是一个或者多个线程的集合,用户可以把需要执行的任务简单地扔给线程池,而不用过多的纠结与执行的细节.那么线程池有哪些作用?或者说与直接用Thread相比,有什么优势?我简单总结了以下几点: 减小线程创建和销毁带来的消耗 对于Java Thread的实现,我在前面的一篇blog中进行了分析.Java Thread与内

Java线程池的几种实现方法及常见问题解答

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

Java线程池使用与原理详解

线程池是什么? 我们可以利用java很容易创建一个新线程,同时操作系统创建一个线程也是一笔不小的开销.所以基于线程的复用,就提出了线程池的概念,我们使用线程池创建出若干个线程,执行完一个任务后,该线程会存在一段时间(用户可以设定空闲线程的存活时间,后面会介绍),等到新任务来的时候就直接复用这个空闲线程,这样就省去了创建.销毁线程损耗.当然空闲线程也会是一种资源的浪费(所有才有空闲线程存活时间的限制),但总比频繁的创建销毁线程好太多. 下面是我的测试代码 /* * @TODO 线程池测试 */ @

Java 线程池详解及实例代码

线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收. 所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁.如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些"池化资源"技术产生的原因. 例如Android中常见到的很多通用组件一般都离不开"池"的概念,如各种图片

Java线程池框架核心代码解析

前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过程和执行过程解耦开来,并用Runnable表示任务. 下面,我们来分析一下 Java 线程池框架的实现ThreadPoolExecutor. 下面的分析基于JDK1.7 生命周期 ThreadPoolExecutor中,使用CAPACITY的高3位来表示运行状态,分别是:  1.RUNNING:接收

Java 线程池原理深入分析

Java 线程池原理 Executor框架的两级调度模型 在HotSpot VM的模型中,Java线程被一对一映射为本地操作系统线程.JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也被销毁回收,而操作系统会调度所有线程并将它们分配给可用的CPU. 在上层,JAVA程序会将应用分解为多个任务,然后使用应用级的调度器(Executor)将这些任务映射成固定数量的线程:在底层,操作系统内核将这些线程映射到硬件处理器上. Executor框架类图 在前面介绍的JA

在spring boot中使用java线程池ExecutorService的讲解

1. 认识java线程池 1.1 在什么情况下使用线程池? 1.单个任务处理的时间比较短 2.需处理的任务的数量大 1.2 使用线程池的好处: 1.减少在创建和销毁线程上所花的时间以及系统资源的开销 2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存 1.3 线程池包括以下四个基本组成部分: 1.线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务: 2.工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以