如何合理估算 Java 线程池大小

前 言

Java 中的线程创建会产生显著的成本。创建线程会消耗时间,增加请求处理的延迟,并且涉及 JVM 和操作系统的大量工作。为了减轻这些开销,线程池发挥了作用。

在本文中,我们将深入研究确定理想线程池大小的艺术。经过微调的线程池可以从系统中提取最佳性能,并帮助我们轻松应对峰值工作负载。然而,重要的是要记住,即使使用线程池,线程的管理本身也可能成为瓶颈。
在这里插入图片描述

1. 使用线程池的原因
性能: 线程的创建和销毁可能会很昂贵,尤其是在 Java 中。线程池通过创建可重复用于多个任务的线程来帮助减少这种开销。
可扩展性: 线程池可以扩展以满足应用程序的需求。例如,在重负载下,可以扩展线程池来处理额外的任务。
资源管理: 线程池可以帮助管理线程使用的资源。例如,线程池可以限制在任何给定时间可以活动的线程数量,这有助于防止应用程序耗尽内存。
2. 调整线程池的大小:了解系统和资源限制
在调整线程池大小时,了解系统的限制(包括硬件和外部依赖性)至关重要。让我们通过一个例子来详细说明这个概念:
场景:
假设我们正在开发一个处理传入 HTTP 请求的Web 应用程序。每个请求可能涉及处理数据库中的数据以及调用外部第三方服务。我们的目标是确定有效处理这些请求的最佳线程池大小。
需要考虑的因素:
**数据库连接池:**假设我们使用 HikariCP 等连接池来管理数据库连接。我们已将其配置为允许最多 100 个连接。如果我们创建的线程多于可用连接,这些额外的线程最终将等待可用连接,从而导致资源争用和潜在的性能问题.
以下是配置 HikariCP 数据库连接池的示例:

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class DatabaseConnectionExample {
    public static void main(String[] args) {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        config.setUsername("username");
        config.setPassword("password");
        config.setMaximumPoolSize(100); // Set the maximum number of connections

        HikariDataSource dataSource = new HikariDataSource(config);

        // Use the dataSource to get database connections and perform queries.
    }
}

外部服务吞吐量: 我们的应用程序与之交互的外部服务有限制。它只能同时处理几个请求,比如一次 10 个请求。同时发送更多请求可能会使服务不堪重负,并导致性能下降或错误。
CPU 核心: 确定服务器上可用的 CPU 核心数量对于优化线程池大小至关重要。

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

每个核心可以同时执行一个线程。超过线程的 CPU 核心数量可能会导致过多的上下文切换,从而降低性能。
3.CPU 密集型任务和 I/O 密集型任务
在这里插入图片描述
CPU密集型任务是那些需要大量处理能力的任务,例如执行复杂的计算或运行模拟。这些任务通常受到 CPU 速度的限制,而不是 I/O 设备的速度。

  • 对音频或视频文件进行编码或解码
  • 编译和链接软件
  • 运行复杂的模拟
  • 执行机器学习或数据挖掘任务
  • 玩电子游戏
    优化:
    多线程和并行性: 并行处理是一种将较大的任务划分为较小的子任务并将这些子任务分布在多个 CPU 核心或处理器上的技术,以利用并发执行并提高整体性能

假设我们有一个很大的数字数组,并且我们想要使用多个线程同时计算每个数字的平方,从而利用并行处理。

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

public class ParallelSquareCalculator {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int numThreads = Runtime.getRuntime().availableProcessors(); // Get the number of CPU cores
        ExecutorService executorService = Executors.newFixedThreadPool(numThreads);

        for (int number : numbers) {
            executorService.submit(() -> {
                int square = calculateSquare(number);
                System.out.println("Square of " + number + " is " + square);
            });
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    private static int calculateSquare(int number) {
        // Simulate a time-consuming calculation (e.g., database query, complex computation)
        try {
            Thread.sleep(1000); // Simulate a 1-second delay
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        return number * number;
    }
}

IO密集型任务是那些与存储设备交互的设备(例如,读/写文件)、网络套接字(例如,进行 API 调用),或用户输入(例如,图形用户界面中的用户交互)。

  • 将大文件读取或写入磁盘(例如,保存视频文件、加载数据库)
  • 通过网络下载或上传文件(例如,浏览网页、观看流媒体视频)
  • 发送和接收电子邮件
  • 运行网络服务器或其他网络服务
  • 执行数据库查询
  • 处理传入请求的 Web 服务器。

优化:
缓存: 将频繁访问的数据缓存在内存中,以减少重复 I/O 操作的需要。
负载平衡: 将 I/O 密集型任务分配到多个线程或进程,以有效处理并发 I/O 操作。
SSD 的使用: 与传统硬盘驱动器 (HDD) 相比,固态驱动器 (SSD) 可以显着加快 I/O 操作速度。
使用高效的数据结构(例如哈希表和 B 树)来减少所需的 I/O 操作数量。
避免不必要的文件操作,例如多次打开和关闭文件。
4、对于 CPU 密集型任务:
对于 CPU 密集型任务,我们希望最大限度地提高 CPU 利用率,但又不会因为过多的线程而压垮系统,否则会导致过多的上下文切换。一个常见的经验法则是使用可用的 CPU 核心数量
现实生活中的例子:视频编码
想象一下我们有一个可用的多核 CPU,此时正在开发一个视频处理应用程序。视频编码是一项 CPU 密集型任务,我们需要应用复杂的算法来压缩视频文件。搜索Java知音公众号,回复“Java题库”,送你一份Java面试宝典
确定 CPU 密集型任务的线程数:
计算可用 CPU 核心数:在 Java 中用于Runtime.getRuntime().availableProcessors()确定可用 CPU 核心的数量。假设我们有 8 个核心。
创建线程池: 创建大小接近或略小于可用CPU核心数的线程池。在这种情况下,我们可以选择 6 或 7 个线程,为其他任务和系统进程留下一些 CPU 容量。

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

public class VideoEncodingApp {
    public static void main(String[] args) {
        int availableCores = Runtime.getRuntime().availableProcessors();
        int numberOfThreads = Math.max(availableCores - 1, 1); // Adjust as needed

        ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);

        // Submit video encoding tasks to the thread pool.
        for (int i = 0; i < 10; i++) {
            threadPool.execute(() -> {
                encodeVideo(); // Simulated video encoding task
            });
        }

        threadPool.shutdown();
    }

    private static void encodeVideo() {
        // Simulate video encoding (CPU-bound) task.
        // Complex calculations and compression algorithms here.
    }
}

5、对于 I/O 密集型任务:
对于 I/O 密集型任务,最佳线程数通常由 I/O 操作的性质和预期延迟决定。我们希望有足够的线程来保持 I/O 设备繁忙而不会使它们过载。理想的数量不一定等于CPU核心的数量。
现实生活中的例子:网页抓取
考虑构建一个网页爬虫来下载网页并提取信息。这涉及到发出HTTP 请求,由于网络延迟,这些请求是 I/O 密集型任务。
确定 I/O 密集型任务的线程数:
分析 I/O 延迟: 估计预期的 I/O 延迟,这取决于网络或存储。例如,如果每个 HTTP 请求大约需要 500 毫秒才能完成,我们可能需要适应 I/O 操作中的一些重叠。

创建线程池: 创建一个大小能够平衡并行性与预期 I/O 延迟的线程池。每个任务不一定需要一个线程;相反,我们可以使用较小的池来有效管理 I/O 密集型任务。

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

public class WebPageCrawler {
    public static void main(String[] args) {
        int expectedIOLatency = 500; // Estimated I/O latency in milliseconds
        int numberOfThreads = 4; // Adjust based on your expected latency and system capabilities

        ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);

        // List of URLs to crawl.
        String[] urlsToCrawl = {
            "https://example.com",
            "https://google.com",
            "https://github.com",
            // Add more URLs here
        };

        for (String url : urlsToCrawl) {
            threadPool.execute(() -> {
                crawlWebPage(url, expectedIOLatency);
            });
        }

        threadPool.shutdown();
    }

    private static void crawlWebPage(String url, int expectedIOLatency) {
        // Simulate web page crawling (I/O-bound) task.
        // Perform HTTP request and process the page content.
        try {
            Thread.sleep(expectedIOLatency); // Simulating I/O latency
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

6、我们可以遵循具体的公式吗?
确定线程池大小的公式可以写成如下:
线程数 = 可用核心数 * 目标 CPU 利用率 * (1 + 等待时间 / 服务时间)

可用核心数: 这是我们的应用程序可用的CPU 核心数。需要注意的是,这与 CPU 的数量不同,因为每个 CPU 可能有多个核心。

目标 CPU 利用率: 这是我们希望应用程序使用的CPU 时间的百分比。如果我们将目标 CPU 利用率设置得太高,我们的应用程序可能会变得无响应。如果设置得太低,我们的应用程序将无法充分利用可用的 CPU 资源。

等待时间: 这是线程等待 I/O 操作完成所花费的时间。这可能包括等待网络响应、数据库查询或文件操作。

服务时间: 这是线程执行计算所花费的时间量。

阻塞系数: 这是等待时间与服务时间的比率。它衡量线程等待 I/O 操作完成所花费的时间相对于执行计算所花费的时间。
7、用法示例
假设我们有一台具有 4 个 CPU 核心的服务器,并且我们希望应用程序使用 50% 的可用 CPU 资源。
我们的应用程序有两类任务:I/O 密集型任务和 CPU 密集型任务。
I/O 密集型任务的阻塞系数为 0.5,这意味着它们花费 50% 的时间等待 I/O 操作完成。

线程数 = 4 核 * 0.5 * (1 + 0.5) = 3 线程

CPU 密集型任务的阻塞系数为 0.1,这意味着它们花费 10% 的时间等待 I/O 操作完成。
线程数 = 4 核 * 0.5 * (1 + 0.1) = 2.2 线程

在此示例中,我们将创建两个线程池,一个用于 I/O 密集型任务,另一个用于 CPU 密集型任务。I/O 密集型线程池将有 3 个线程,CPU 密集型线程池将有 2 个线程。

这是根据大量的案例总结的Java线程池大小确定的公式,但在实操中所考虑的侧重点可能有不同,那么需要根据实际场景来微调,本文提供一种确定最优的思路,希望对你开发中确定线程池大小有所帮助!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++】模版-初阶

目录 泛型编程--模版 函数模版 类模版 泛型编程--模版 函数模版 如何实现一个通用的交换函数呢?void Swap(int& left, int& right){int temp left;left right;right temp;}void Swap(double& left, double& right){double temp left;left right;righ…

基于ssm+vue交通事故档案系统

摘要 摘要是对文章、论文或其他文本的主要观点、结论和关键信息的简洁概括。由于你没有提供具体的文章或主题&#xff0c;我将为你创建一个通用的摘要。 本文介绍了一种基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;和Vue.js的交通事故档案管理系统的设计与实现…

设计模式-备忘录模式-笔记

动机&#xff08;Motivation&#xff09; 在软件构建过程中&#xff0c;某些对象的状态在转换过程中&#xff0c;可能由于某种需要&#xff0c;要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态&#xff0c;便会暴露对象的细节…

ROS服务(Service)通信:通信模型、Hello World与拓展

服务通讯是基于请求响应模式的&#xff0c;是一种应答机制。 用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。 一、服务通讯模型 服务是一种双向通讯方式&#xff0c;它通过请求和应答的方式传递消息&#xff0c;该模型涉及到三个角色&#xff1a; Master…

USART(1)

什么是USART 单片机上有的许多的外设 单片机通过这些外设实现特殊的功能 如果单片机想要和蓝牙模块实现数据的传输那么就也需要单片机有串口模块来和蓝牙模块的串口进行连接 相互传输数据 在单片机上的串口就叫USART USART就是单片机上的外设 来实现串口之间的通信功能 USART名…

基于51单片机步进电机节拍步数正反转LCD1602显示( proteus仿真+程序+原理图+设计报告+讲解视频)

基于51单片机步进电机节拍步数正反转LCD1602显示 &#x1f4d1;1. 主要功能&#xff1a;&#x1f4d1;2. 讲解视频&#xff1a;&#x1f4d1;3. 仿真&#x1f4d1;4. 程序代码&#x1f4d1;5. 设计报告&#x1f4d1;6. 设计资料内容清单&&下载链接&#x1f4d1;[资料下…

基于SSM的宠物医院管理系统

基于SSM的宠物医院管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringMyBatisSpringMVC工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 后台 摘要 随着人们对宠物健康关注的增加&#xff0c;宠物医疗服务的需求也…

许战海战略文库|企业竞争优势三大获取路径:产业、品牌和产品竞争优势

在快速发展和变革的全球化市场中&#xff0c;企业面临着持续的竞争压力。要在这种环境中脱颖而出&#xff0c;企业需要建立持久的竞争优势。通常&#xff0c;竞争优势可以从三个主要路径来获取&#xff1a;产业竞争优势、品牌竞争优势和产品竞争优势。 1. 产品竞争优势为什么很…

电子科技大学 分布式系统 期末复习笔记

第一章 为什么需要分布式系统&#xff1a;功能分离&#xff0c;固有的分布性&#xff0c;负载均衡&#xff0c;可靠性&#xff0c;经济性。 定义&#xff1a;分布式系统是这样一种系统&#xff0c;其中位于联网计算机上的组件仅通过传递消息来通信和协调它们的操作。 特点&am…

线性表的概念

目录 1.什么叫线性表2.区分线性表的题 1.什么叫线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是…

【Vue配置项】 computed计算属性 | watch侦听属性

目录 前言 computed计算属性 什么是计算属性&#xff1f; Vue的原有属性是什么&#xff1f; 得到的全新的属性是什么&#xff1f; 计算属性怎么用&#xff1f; 计算属性的作用是什么&#xff1f; 为什么说代码执行率高了&#xff1f; computed计算属性中的this指向 co…

【Java 进阶篇】JQuery 遍历 —— 无尽可能性的 `each` 之旅

在前端的征途中&#xff0c;操作元素是开发者不可避免的任务之一。而在 JQuery 中&#xff0c;each 方法则是处理这个任务的得力助手。本文将深入探讨 each 方法的奇妙之处&#xff0c;以及它与原生的 for...of 循环的关系&#xff0c;带你领略无尽可能性的遍历之旅。 起步&am…

modbusRTU通信简单实现(使用NModbus4通信库)

本文实现ModbusRTU通信&#xff0c;使用的是NModbus4通信库&#xff0c;使用 Modbus Slave是一个模拟Modbus协议从机的上位机软件&#xff0c;主要用于模拟测试跟其他主机设备通信的过程。与之成套存在的另一个软件--Modbus Poll&#xff0c;则是模拟Modbus协议主机的上位机软件…

元宇宙数字展厅无代码编辑工具的功能特点

商场如战场&#xff0c;营销是每个企业都必须重视的环节。随着科技的发展&#xff0c;3D展示营销制作平台作为企业快速搭建3D互动展厅的SaaS平台&#xff0c;逐渐崭露头角&#xff0c;为企业提供了诸多便利&#xff0c;让营销变得更加高效和引人入胜。 为企业提供身临其境的产品…

【EI会议征稿】第五届人工智能与机电自动化国际学术会议(AIEA 2024)

第五届人工智能与机电自动化国际学术会议&#xff08;AIEA 2024&#xff09; 2024 5th International Conference on Artificial Intelligence and Electromechanical Automation 第五届人工智能与机电自动化国际学术会议&#xff08;AIEA 2024&#xff09;将于2024年3月8-10…

算法竞赛备赛进阶之状态机模型训练

目录 1.大盗阿福 2.股票买卖IV 3.股票买卖V 4.设计密码 算法状态机&#xff08;ASM&#xff09;图是一种描述时序数字系统控制过程的算法流程图&#xff0c;其结构形式类似于计算机中的程序流程图。 ASM图是用一些特定符号按规定的连接方式来描述数字系统的功能。应用ASM图…

基于JavaWeb+SSM+购物系统微信小程序的设计和实现

基于JavaWebSSM购物系统微信小程序的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 第一章 绪 论 1.1选题背景 互联网是人类的基本需求&#xff0c;特别是在现代社会&#xff0c;…

信号的机制——信号处理函数的注册

在 Linux 操作系统中&#xff0c;为了响应各种各样的事件&#xff0c;也是定义了非常多的信号。我们可以通过 kill -l 命令&#xff0c;查看所有的信号。 # kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS …

计算机毕业设计 基于SpringBoot的医院档案管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

使用 SMI 指标增强股票分析:amCharts JS Crack

使用 SMI 指标增强股票分析 2023 年 11 月 16 日 amCharts 5&#xff1a;股票图表 v5.5.3 增加了对随机动量指数指标的支持&#xff0c;帮助用户做出更明智的交易决策。 amCharts 5&#xff1a;股票图表提供了用于显示基于时间的数据的分析工具&#xff0c;无论是金融、股票还是…