iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >简单聊一聊Java线程池ThreadPoolExecutor
  • 521
分享到

简单聊一聊Java线程池ThreadPoolExecutor

2024-04-02 19:04:59 521人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录简介参数说明如何创建线程池拒绝策略总结简介 ThreadPoolExecutor是一个实现ExecutorService接口的线程池,ExecutorService是主要用来处理

简介

ThreadPoolExecutor是一个实现ExecutorService接口的线程池,ExecutorService是主要用来处理多线程任务的一个接口,通常比较简单是用法是由Executors工厂类去创建。

线程池主要解决了两个不同的问题:

  • 在执行大量异步任务时,为了能够提高性能,通常会减少每个任务的调用开销。
  • 提供了一系列多线程任务的管理方法,便于多任务执行时合理分配资源以及一些异常情况的处理。每个ThreadPoolExecutor还维护一些基本统计信息。例如:已完成任务的数量,当前获得线程数等。

参数说明

ThreadPoolExecutor提供了几个核心参数,方便开发人员根据具体场景合理分配线程资源。

  • corePoolSize:核心线程数,在线程池创建时就已初始化好的n个核心线程,即使线程空闲着也会一直保留在线程池中不被销毁,除非调用线程池方法设置了java.util.concurrent.ThreadPoolExecutor#allowCoreThreadTimeOut(true)(允许核心线程超时销毁)。
  • maximumPoolSize:线程池允许存在最大线程数。
  • keepAliveTime:当线程数大于核心线程数时,多余的线程在执行任务结束后等待新任务的最大等待时间。
  • unitTimeUnit类型,是keepAliveTime多余线程最大空余时间单位。
  • workQueue:必须指定一个阻塞队列,在线程池执行execute方法时新进来的任务在执行前都会保留到此队列里进入等待。
  • threadFactory:创建线程的工厂,默认采用Executors.defaultThreadFactory()创建线程。
  • handler:拒绝策略,当最大线程数已占满,且队列已满,此时线程池将触发拒绝策略,对新进来的任务做拒绝处理,具体的处理方案在后面详细分析(默认使用java.util.concurrent.ThreadPoolExecutor.AbortPolicy直接抛出异常拒绝处理)。

注:maximumPoolSize如果大于corePoolSize,则多出的部分线程数只有在阻塞队列workQueue占满时才会创建核心线程之外的线程去执行任务,如果我们设置的阻塞队列为无界队列(默认大小为Integer.MAX_VALUE),则队列永远无法占满,就不会去创建额外的线程进行工作,一般情况如果任务数足够,那么也是在队列大小还没达到Integer.MAX_VALUE时就已经出现内存溢出了。Executors线程池工厂中的newFixedThreadPool()、newSingleThreadExecutor()方法就是使用了无界队列LinkedBlockingQueue,防止内存溢出在日常开发过程中一般是不建议直接去使用Executors去创建线程池。

如何创建线程池

上面我们提到的可以使用Executors工厂直接创建线程池,但是Executors提供的创建线程池都是不可控的,我们还是得按自己的业务做好分析自定义一个线程池。

以下是线程池创建的一个案例:

@Slf4j
@Configuration
public class ThreadPoolConfig {

    @Value("${threadPool.corePoolSize:8}")
    private int corePoolSize;

    @Value("${threadPool.maximumPoolSize:16}")
    private int maximumPoolSize;

    @Value("${threadPool.keepAliveTime:60}")
    private int keepAliveTime;

    @Value("${threadPool.queueSize:99999}")
    private int queueSize;

    @Bean
    public ThreadPoolExecutor testExecutor() {
        LinkedBlockingQueue queue = new LinkedBlockingQueue(queueSize);
        return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, 
                TimeUnit.SECONDS, queue, getThreadFactory(), getRejectedExecutionHandler());
    }

    
    private ThreadFactory getThreadFactory() {
        return new ThreadFactory() {

            @Override
            public Thread newThread(Runnable r) {
                log.info("===> Create new thread ...");
                return new Thread(r);
            }
        };
    }

    
    private RejectedExecutionHandler getRejectedExecutionHandler() {
        return new RejectedExecutionHandler() {

            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                // 继续往队列里添加任务,这里只是一个案例,这种方式并不友好,会抛出队列已满的异常
                log.info("===> Handler runnable ......");
                executor.getQueue().add(r);
            }
        };
    }
}

application.properties配置文件

threadPool:
  corePoolSize: 8
  maximumPoolSize: 16
  keepAliveTime: 60
  # 为方便测试这里我们配置队列数小一点
  queueSize: 99

由以上的线程池配置,我们写一个demo测试一下:

截取部分运行日志

  • 红框我们可以看到执行到了线程创建工厂部分代码块
  • 蓝色框日志我们可以看到largestPoolSize=11,这是由于我们配置的maximumPoolSize=16 > corePoolSize=8,我们demo执行的是110个任务并发,队列大小是99,由此分析得出(需要额外出创建线程数 = 并发任务总数110 - 核心线程数8 - 队列大小99 = 3),所以线程池在队列已满时会多创建3个线程用于执行任务,在达到keepAliveTime配置的最大空闲时间后这3个线程即会自动销毁。

注:可能有的同学会想线程池使用后需要销毁吗?在这里补充一下,如果我们是作为局部变量创建出来的线程池(如:在执行的方法内使用Executors.newFixedThreadPool(10)创建临时的线程池),这种情况我们用完就必须将它立即销毁,否则主线程就会一直处于运行状态。如果是全局配置的线程池,那么就是为整个系统中诸多业务提供使用的,这种就不需要对线程池做销毁,因为一旦销毁了其他的任务就无法继续使用该线程池执行任务。

  • 销毁线程池主要有两种方式:
    • shutdown():此方法对线程池做销毁,线程池会优先将剩余未完成的任务执行完才会执行销毁任务。
    • shutdownNow():此方法会对线程池做立即销毁,无论线程池中的任务是否执行完成。

拒绝策略

通常我们在配置好有限队列大小后,就会有可能出现队列占满的情况,这时候我们的拒绝策略就会起到作用,接下来我们就来分析一下RejectedExecutionHandler接口具体有哪一些实现方式:

  • AbortPolicy:线程池的默认拒绝策略,在jdk提供的ThreadPoolExecutor线程池中有一个默认线程池变量private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();作为默认拒绝策略,查看如下图源码可知它就是直接抛出RejectedExecutionException异常,并且会直接丢弃当前任务,如果担心异常影响后续任务执行开发人员需自行捕获异常处理

  • CallerRunsPolicy:只要在当线程池未被销毁的情况下,不丢弃任务直接使用主线程(调用线程池执行的线程)执行该任务。因为该策略是由主线程直接执行任务的,所以不建议在并发度高的情况下使用,建议在并发度较低且任务不允许失败的情况下才使用此策略

  • DiscardPolicy:直接丢弃当前任务,不做任何处理。直接丢弃任务的情况下,开发人员也无法排查到哪些任务被丢弃掉,一般不建议使用,除非是无关紧要的任务即使丢弃也无所谓的。

  • DiscardOldestPolicy:在线程池未被销毁的情况下,丢弃最早进入队列的一个任务(即最久未执行的任务),然后再重新将此任务加入线程池,在此策略下需注意被丢弃的任务的重要性,如果任务不重要可直接丢弃。

  • 自定义策略:在以上JDK提供的四种默认拒绝策略之外,我们还可以通过自定义的方式来处理被拒绝的任务。如果担心任务被拒绝或者被丢弃造成不可预估的问题,在时效性没有太大要求的情况下我们可以先将任务内容转换成数据入库做好日志记录,后续可以使用定时任务或者通过MQ消息延迟处理。由以上的线程池配置Demo中的拒绝策略改造伪代码如下:
private RejectedExecutionHandler getRejectedExecutionHandler() {
    return new RejectedExecutionHandler() {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            // 伪代码
            log.info("===> 可根据任务的重要性区分对待,将任务做转换入库延迟处理 ......");
        }
    };
}

总结

线程池是为了充分利用CPU资源,在合理分批使用的情况下能够极大的提高我们程序的性能,以上的参数配置仅作为参考,并没有一个标准的依据,只能在实际开发过程中开发人员自行多做一些测试来判断参数如何配置更加合理。

在拒绝策略配置方面,如果被拒绝的任务相对紧急且重要不可丢弃的情况下,此类任务可独立做一个线程池处理保证任务不丢失,程序只能慢慢优化变得越来越好,不可能有完美的程序即保证高性能又保证安全可靠。

到此这篇关于Java线程池ThreadPoolExecutor的文章就介绍到这了,更多相关Java线程池ThreadPoolExecutor内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 简单聊一聊Java线程池ThreadPoolExecutor

本文链接: https://www.lsjlt.com/news/151149.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 简单聊一聊Java线程池ThreadPoolExecutor
    目录简介参数说明如何创建线程池拒绝策略总结简介 ThreadPoolExecutor是一个实现ExecutorService接口的线程池,ExecutorService是主要用来处理...
    99+
    2024-04-02
  • 简单聊一聊Vue3组件更新过程
    目录前言副作用渲染函数更新组件的过程核心逻辑:patch流程1.处理组件2.处理普通元素总结前言 组件渲染的过程,本质上就是把各种把各种类型的 vnode 渲染成真实 DOM。我们也...
    99+
    2024-04-02
  • Java线程池 ThreadPoolExecutor 详解
    目录一 为什么要使用线程池二 线程池原理详解2.1 线程池核心组成2.2 Execute 原理三 线程池的使用3.1 创建线程池3.1.1 自定义线程池3.1.2 功能线程池3.1....
    99+
    2024-04-02
  • 简单谈谈ThreadPoolExecutor线程池之submit方法
    jdk1.7.0_79 在上一篇《ThreadPoolExecutor线程池原理及其execute方法》中提到了线程池ThreadPoolExecutor的原理以及它的execute方法。本文解析ThreadPoolExecutor#sub...
    99+
    2023-05-31
    线程池 submit threadpoolexecutor
  • 简单聊聊Java程序中的换行符
    目录常见的换行符\r\n和\n的区别换行符的解析小结常见的换行符 Java程序中的换行符一般使用“\n”表示,它是一个转义字符,表示换行符。根据操作系统的不同...
    99+
    2023-03-21
    Java 换行符使用 Java换行符
  • 聊聊Redis的单线程模型
    目录开篇正文文件事件处理器redis 事件处理伪代码redis 源码总结开篇 本文主要来探讨一下 redis 的单线程模型,文章前半部分会先引用某网络课程讲解的内容(图片+语言描述)...
    99+
    2022-12-19
    Redis单线程 Redis单线程模型
  • python3-001-多线程简单聊天室
            这次代码为python3编写,并且使用网络调试助手、与python3聊天室程序进行通信测试。1、先放干货代码,如下: 1 from socket import * 2 from threading import Th...
    99+
    2023-01-31
    多线程 聊天室 简单
  • 简单聊一聊原生Ajax与JQuery Ajax
    目录前言一、Ajax简介。二、Ajax概念1、XMLHttpRequest对象2、HTTP请求3、XHR用法三、jQuery-AJAX总结前言 没有学Ajax之前,就在想这到底是一门...
    99+
    2024-04-02
  • Java实现单线程聊天室
    本文实例为大家分享了Java实现单线程聊天室的具体代码,供大家参考,具体内容如下 一. Socket API简介 1. Socket编程 Java.net.Socket类代表一个套接...
    99+
    2024-04-02
  • 简单聊一聊Node.js参数max-old-space-size
    目录前言设置环境变量命令行方式第三种方式,基于项目总结前言 Old space是 V8 托管(也称为垃圾收集)堆(即 JavaScript 对象所在的位置)中最大和最可配置的部分,而...
    99+
    2023-01-28
    node.js参数max-old-space-size nodejs 参数 nodejs命令
  • 简单聊一聊redis过期时间的问题
    目录1.多次修改一个Redis的String过期键,如何保证他仍然能保留第一次设置时的删除时间2.修改hash、set、Zset、list的值,会使过期时间重置吗?总结1.多次修改一个redis的String过期键,如何...
    99+
    2023-04-14
    redis 过期时间 redis 过期时间 java redis设置过期时间
  • Java简单实现线程池
    本文实例为大家分享了Java简单实现线程池的具体代码,供大家参考,具体内容如下 一、线程池 线程池是一种缓冲提高效率的技术。 相当于一个池子,里面存放大量已经创建好的线程,当有一个任...
    99+
    2024-04-02
  • java NIO实现简单聊天程序
    本文实例为大家分享了java NIO实现简单聊天程序的具体代码,供大家参考,具体内容如下 服务端 功能: 1、接受客户端连接 2、发送消息 3、读取客户端消息 Server.jav...
    99+
    2024-04-02
  • Java ThreadPoolExecutor线程池有关介绍
    目录为什么要有线程池线程池状态ThreadPoolExecutor核心参数corePoolSizemaximumPoolSizekeepAliveTimeunitworkQueuet...
    99+
    2024-04-02
  • java线程池ThreadPoolExecutor类怎么用
    这篇文章将为大家详细讲解有关java线程池ThreadPoolExecutor类怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许...
    99+
    2023-06-29
  • Java网络编程实例——简单模拟在线聊天
    目录1、前提知识 2、实现思路: 1、前提知识 需要知道简单的IO流操作,以及简单的UDP发送数据包的原理。 需要用到的类:DatagramSocket、DatagramPacke...
    99+
    2024-04-02
  • 简单聊一聊axios配置请求头content-type
    目录前言开始使用总结前言 现在前端开发中需要通过Ajax发送请求获取后端数据是很普遍的一件事情了,鉴于我平时在撸码中用的是vue技术栈,今天这里来谈谈我们常用的发Ajax请求的一个插...
    99+
    2024-04-02
  • 简单聊一聊vue中data的代理和监听
    目录假设现在有一个data需求一: 用 Object.defineProperty 定义 n需求二:n不能小于0所以就有了需求三:不暴露data中可以设置的属性,而是使用一个代理因此...
    99+
    2024-04-02
  • 简单聊一聊SQL注入及防止SQL注入
    目录SQL注入附防止sql注入的一些建议总结SQL注入 SQL注入是通过操作输入来修改事先定义好的SQL语句,对用户输入的字符串进行过滤,转义,限制或处理不严谨,导致用户可以通过输入...
    99+
    2024-04-02
  • Java线程池ThreadPoolExecutor怎么创建
    本篇内容介绍了“Java线程池ThreadPoolExecutor怎么创建”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!简介ThreadPo...
    99+
    2023-07-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作