【Java开发笔记】线程池

news2024/11/15 11:17:15

【Java开发笔记】线程池

线程池 ThreadPoolExecutor 的七大核心参数:

  • 核心线程数 corePoolSize
  • 最大线程数 maxinumPoolSize
  • 超过核心线程数的闲余线程存活时间 keepAliveTime
  • 存活时间单位 unit:keepAliveTime
  • 任务队列(阻塞队列) workQueue
  • 生成线程池中工作线程的线程工厂 threadFactory
  • 拒绝策略 handler

image-20230212144932038

1 线程池扩容流程概述

image-20230212140140002

  1. 当线程池创建完成后,池内的线程数量为 0 ,这是因为采用了 懒加载 机制。
  2. 当一个任务通过 submit()execute() 方法提交到线程池后:
    • 如果当前池中线程数(包括闲置线程)小于 corePoolSize,则创建一个新线程执行任务
    • 如果当前线程池中的线程数已经达到了 corePoolSize,则将任务放入等待队列
    • 如果任务队列已满,则任务无法放入等待队列。此时如果线程池中线程数量小于 maxPoolSize,则创建一个临时线程执行任务,临时线程数量 = 最大线程数 - 核心线程数
    • 如果当前池中线程总数已经等于 maxPoolSize,此时无法执行该任务,触发拒绝策略进行处理
  3. 当线程池中线程数量大于 corePoolSize 后,超过 keepAliveTime 时间的闲置线程会被回收掉,回收的是非核心线程(即临时线程),核心线程一般是不会被回收的。如果设置 allowCoreThreadTimeout(True),则核心线程数也会被回收。

2 线程池的拒绝策略

2.1 什么时候会触发拒绝策略?

有两种情况:

  1. 当我们调用 shutdown 等方法关闭线程池后,如果再向线程池内提交任务,则会遭到拒绝
  2. 线程池没有空闲线程(即线程数量达到 maxPoolSize 并且都在执行任务),并且任务队列已满,则会遭到拒绝

2.2 有哪些拒绝策略?

AbortPolicy(默认)

直接抛出异常,中断程序执行

这种拒绝策略在拒绝任务时,会直接抛出一个类型为 RejectedExecutionExceptionRuntimeException,让你感知任务被拒绝了,于是便可根据业务逻辑选择重试或者放弃提交策略。

		public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

DiscardPolicy

直接丢弃,不抛异常

这种拒绝策略在拒绝任务时,直接丢弃,不会给任何通知,相对而言有一定风险。因为我们对任务丢弃是无感的,可能会造成数据丢失。

		public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

DiscardOldestPolicy

丢弃任务队列中最老的节点

丢弃 任务队列 中的头节点。通常是存活时间最长的任务,它也存在一定的数据丢失风险。

		public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

CallerRunsPolicy(推荐)

当前线程处理不了,回调主线程,让主线程处理任务

当有新任务提交后,如果线程池没有被关闭且没有能力执行,则把这个任务交于提交任务的线程(主线程)执行。也就是谁提交任务,谁执行。主线程在处理任务时,可以为线程池腾出时间,如果此时有新的空闲线程,那么可继续协助主线程处理任务。

		public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

自定义拒绝策略

实现 RejectedExecutionHandle 接口来实现自己的拒绝策略。

3、Spring内置的线程池

我们实际开发中更多的是使用 SpringBoot 来开发,Spring 默认也是自带了一个线程池方便我们开发,它就是 ThreadPoolTaskExecutorThreadPoolTaskExecutor 是对 ThreadPoolExecutor 进行了封装处理。

// 获取线程池中当前线程数
int poolSize = threadPoolTaskExecutor.getThreadPoolExecutor().getPoolSize();
System.out.println("当前线程数量" + poolSize);

// 当前压入队列的任务数
int size = threadPoolTaskExecutor.getThreadPoolExecutor().getQueue().size();
System.out.println("当前压入等待队列的线程数量" + size);

// 获取当前的活跃线程数(正在处理任务线程)
int activeCount = threadPoolTaskExecutor.getThreadPoolExecutor().getActiveCount();
System.out.println("当前活跃线程数" + activeCount);

// 获取当前完成的任务数
long completedTaskCount = threadPoolTaskExecutor.getThreadPoolExecutor().getCompletedTaskCount();
System.out.println("完成任务数" + completedTaskCount);

// 获取总任务数
long taskCount = threadPoolTaskExecutor.getThreadPoolExecutor().getTaskCount();
System.out.println("总任务数" + taskCount);

4、线程池参数设置原则

4.1 线程数量

设置多少个线程数量通常是根据应用的类型进行设计:

  • IO密集型2n+1n 为 CPU 核数。

    在开发中较为常见,如 mysql 数据库的读写、文件的读写、网络通信等任务。这些任务不会特别消耗 CPU 资源,但 IO 操作比较耗时,会占用较多时间。

  • CPU密集型n+1n 为 CPU 核数

    场景如解密、加密、压缩、计算等。

可以通过下面 api 查看当前服务器的 CPU 核数:

int core = Runtime.getRuntime().availableProcessors();

4.2 无界队列

实际运行中,我们一般会设置线程池的阻塞队列长度,如果不设置,ThreadPoolTaskExecutor 用默认值:

private int corePoolSize = 1;
private int maxPoolSize = Integer.MAX_VALUE;
private int keepAliveSeconds = 60;
private int queueCapacity = Integer.MAX_VALUE;

注意:不能设置队列长度过大,否则可能会造成内存溢出(OOM)的错误!

所以,禁止使用默认的队列长度!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/341225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内网渗透(二十)之Windows协议认证和密码抓取-域认证(Kerberos协议)

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

【LeetCode】1138. 字母板上的路径

1138. 字母板上的路径 题目描述 我们从一块字母板上的位置 (0, 0) 出发,该坐标对应的字符为 board[0][0]。 在本题里,字母板为board [“abcde”, “fghij”, “klmno”, “pqrst”, “uvwxy”, “z”],如下所示。 我们可以按下面的指令规…

Spring Security in Action 第一、二章 第一个Spring Security项目的建立以及基本

本专栏将从基础开始,循序渐进,以实战为线索,逐步深入SpringSecurity相关知识相关知识,打造完整的SpringSecurity学习步骤,提升工程化编码能力和思维能力,写出高质量代码。希望大家都能够从中有所收获&#…

Docker 搭建本地私有仓库

一、搭建本地私有仓库有时候使用Docker Hub这样的公共仓库可能不方便,这种情况下用户可以使用registry创建一个本地仓库供私人使用,这点跟Maven的管理类似。使用私有仓库有许多优点:1)节省网络带宽,针对于每个镜像不用…

小灰的算法之旅---createBinaryTree 的一点点疑问

前言 深知自己算法薄弱,所以最近在补充自己算法方面的知识,《小灰的算法之旅》这本书作为入门书籍不错,当时在看到《树-深度优先遍历》的代码时,我碰到了一点疑问,经过我多次代码验证,确实是代码不太严谨。…

C语言基础(有基础)

linux下的 是一种通用的、面向过程式的计算机编程语言 #include <stdio.h> //#include 预处理命令&#xff0c;用来引用头文件&#xff0c; stdio.h 头文件 int main() //开始 {/* 一个注释 */printf("Hello, World! \n");return 0; …

docker安装mysql

在安装Mysql之前&#xff0c;我们可以先查看一下我们的镜像&#xff0c;输入命令&#xff1a; docker images 能发现&#xff0c;镜像里面只有一个Nginx&#xff0c;并没有Mysql 然后我们可以像上一篇安装Nginx一样&#xff0c;安装Mysql镜像。 输入以下命令&#xff0c;安装…

B站Python与OpenCV人脸识别项目超详细记录(对图片、视频、摄像头人脸的检测)

课程来源&#xff1a;一天搞定人脸识别项目&#xff01;学不会up直接下跪&#xff01;&#xff08;pythonopencv&#xff09;_哔哩哔哩_bilibili 图片来源&#xff1a;感谢王鹤棣先生友情出镜~ 环境配置详见&#xff1a; 在conda虚拟环境中安装OpenCv并在pycharm中使用_cond…

已解决io.UnsupportedOperation: not readable

已解决Python读取文件报错&#xff1a;io.UnsupportedOperation: not readable亲测有效 文章目录报错问题报错翻译报错原因解决方法联系博主免费帮忙解决报错报错问题 一个小伙伴遇到问题跑来私信我&#xff0c;想用Python读取文件&#xff0c;但是发生了报错&#xff08;当时他…

跟同事杠上了,Apache Beanutils为什么被禁止使用?

收录于热门专栏Java基础教程系列&#xff08;进阶篇&#xff09; 在实际的项目开发中&#xff0c;对象间赋值普遍存在&#xff0c;随着双十一、秒杀等电商过程愈加复杂&#xff0c;数据量也在不断攀升&#xff0c;效率问题&#xff0c;浮出水面。 问&#xff1a;如果是你来写…

Redis过期删除策略

目录引出Redis过期删除策略Redis的两种过期策略&#xff1a;定期删除 惰性删除定期删除惰性删除Redis两种过期删除策略存在的问题Redis缓存淘汰策略Redis中的LRU和LFU算法1、LRU&#xff08;Least Recently Userd最近最少使用&#xff09;LFU 算法的引入2、LFU&#xff08;lea…

Netty 组件学习

Netty 组件学习Netty 各个组件通俗理解EventLoopEventLoopGroup关闭ChannelFuture & PromiseHandler & PipelineByteBuf创建直接内存和堆内存池化和非池化组成方法扩容机制读取retain和release方法Netty 各个组件通俗理解 Channel即数据通道 Msg是数据&#xff0c;传…

对KMP简单的理解

声明&#xff1a;下边的例子均表示下标从1开始的数组 ne数组的定义&#xff1a; next[i] 就是使子串 s[1…i] 有最长相等前后缀的前缀的最后一位的下标。ne[i]也可以表示相等子串的长度 准备执行jne[j]时&#xff0c; 表示当前s[i]!p[j1] , 如果ne[j]1 &#xff0c;那么下…

Dubbo和Zookeeper集成分布式系统快速入门

文件结构 代码部分 1、新建provider-server导入pom依赖 <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.3</version></dependency><dependency>&l…

golang的web框架Gin(一)---Gin的安装与初体验

简介 1.1 介绍 Go世界里最流行的Web框架&#xff0c;Github上有32Kstar。 基于httprouter开发的Web框架。 中文文档齐全&#xff0c;简单易用的轻量级框架。 Gin是一个golang的微框架&#xff0c;封装比较优雅&#xff0c;API友好&#xff0c;源码注释比较明确&#xff0c;具有…

C++模板初阶

C模板初阶泛型编程函数模板概念函数模板格式函数模板原理函数模板的实例化模板参数的匹配原则类模板类模板的定义格式类模板的实例化泛型编程 我们前面学习了C的函数重载功能&#xff0c;那么我们如何实现一个通用的交换函数呢&#xff0c;比如:我传入int就是交换int&#xff…

JavaSE XML语法规则和文档约束介绍

文章目录XMLXML基本介绍XML创建和语法规则XML文档约束认识文档约束DTD约束(了解)schema约束(了解)XML XML基本介绍 XML概述: XML是可扩展标记语言&#xff08;eXtensible Markup Language&#xff09;的缩写&#xff0c;它是一种可以自定义数据的表示格式&#xff0c;可以描述…

【mysql数据库】

目录SQL数据库分页聚合函数表跟表之间的关联关系SQL中怎么将行转成列SQL注入将一张表的部分数据更新到另一张表WHERE和HAVING的区别索引索引分类如何创建及保存MySQL的索引&#xff1f;怎么判断要不要加索引&#xff1f;索引设计原理只要创建了索引&#xff0c;就一定会走索引吗…

ESP-01S通过AT指令上报数据到阿里云物模型

ESP-01S使用AT指令上报数据到阿里云物模型 上篇文章介绍了如何用AT指令连接阿里云并进行通信&#xff1a;https://blog.csdn.net/weixin_46251230/article/details/128995530 但最终需要将传感器数据上报到云平台显示&#xff0c;所以需要建立阿里云物模型 阿里云平台建立物…

代码随想录第62天(单调栈):● 739. 每日温度 ● 496.下一个更大元素 I

今天开启单调栈的篇章&#xff0c;一般什么时候采用单调栈&#xff1f;通常是一维数组&#xff0c;要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。 一、每日温度 题目描述&#xff1a; 思路和想法&#xff1a; 这里单调栈里只放数组下标&#xff0c;再了…