0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

怎么在JAVA中确定线性池大小

FPGA研究院 来源:Java学研大本营 2024-10-24 14:02 次阅读

JAVA中确定线性池大小,分别介绍CPU密集型任务和I/O密集型任务及其处理方法

在Java中,线程创建会产生显著的开销。创建线程会消耗时间,增加请求处理的延迟,并且涉及JVM和操作系统的相当多工作。为减轻这些开销,线程池应运而生。

线程池(ThreadPool)是由执行器服务(executor service)管理的工作线程池。其理念是重用现有线程,而不是为每个任务创建新线程。这可以通过减少线程创建的开销来显著提高应用程序的性能。Java的ExecutorService和ThreadPoolExecutor类提供了管理线程池的框架。

关键点

线程重用:线程池的线程可用于多个任务的重用。

任务排队:任务被提交到池中,池中的线程会提取并执行这些任务。

资源管理:可配置线程池大小、任务队列大小和其他参数,以高效管理资源。

1. 使用线性池的原因

性能:线程的创建和销毁成本较高,尤其在Java中。创建一个可供多任务重用的线程池可减少这种开销。

可扩展性:线程池可根据应用程序的需要进行扩展。例如,在负载较重时,可扩展线程池以处理额外的任务。

资源管理:线程池可帮助管理线程使用资源。例如,线程池可限制同时活动的线程数量,防止应用程序内存不足。

2. 确定线程池大小:理解系统和资源限制

了解包括硬件和外部依赖关系的系统限制,对于确定线程池大小至关重要。下面通过一个例子来详细说明这一概念。

假设在开发一个处理传入HTTP请求的Web应用程序,每个请求可能涉及数据库处理数据和调用外部第三方服务。目标是确定处理这些请求的最佳线程池大小。

此情况下需考虑因素包含数据库连接池与外部服务吞吐量两方面。

数据库连接池:假设使用HikariCP之类的连接池来管理数据库连接。您已将其配置为允许最多100个连接。如果您创建的线程超过可用连接数,这些额外的线程将会等待可用连接,导致资源争用和潜在的性能问题。

以下是配置HikariCP数据库连接池的示例。

importcom.zaxxer.hikari.HikariConfig;
importcom.zaxxer.hikari.HikariDataSource;

publicclassDatabaseConnectionExample{
publicstaticvoidmain(String[]args){
HikariConfigconfig=newHikariConfig();
config.setJdbcUrl("jdbc//localhost:3306/mydb");
config.setUsername("username");
config.setPassword("password");
config.setMaximumPoolSize(100);//设置最大连接数

HikariDataSourcedataSource=newHikariDataSource(config);

//使用 dataSource 获取数据库连接并执行查询。
}
}

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

intnumOfCores=Runtime.getRuntime().availableProcessors();

每个核心可以同时执行一个线程。超出CPU核心数量的线程会导致过度的上下文切换,从而降低性能。

3.CPU密集型任务与I/O密集型任务

CPU密集型任务

CPU密集型任务是那些需要大量处理能力的任务,例如执行复杂计算或运行模拟。这些任务通常受限于CPU的速度,而不是I/O设备的速度,如下列任务。

编码或解码音频视频文件

编译和链接软件

运行复杂模拟

执行机器学习或数据挖掘任务

玩视频游戏

要对CPU密集型任务进行优化,应考虑多线程和并行性。并行处理是一种技术,用于将较大的任务划分为较小的子任务,并将这些子任务分配到多个CPU核心或处理器上,以利用并发执行并提高整体性能。假设有一个大型数字数组,要使用多个线程并发计算每个数字的平方,以利用并行处理,示例代码如下。

importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.TimeUnit;

publicclassParallelSquareCalculator{
publicstaticvoidmain(String[]args){
int[]numbers={1,2,3,4,5,6,7,8,9,10};
intnumThreads=Runtime.getRuntime().availableProcessors();//获取CPU核心数量
ExecutorServiceexecutorService=Executors.newFixedThreadPool(numThreads);

for(intnumber:numbers){
executorService.submit(()->{
intsquare=calculateSquare(number);
System.out.println("Squareof"+number+"is"+square);
});
}

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

privatestaticintcalculateSquare(intnumber){
//模拟耗时计算(例如,数据库查询、复杂计算)
try{
Thread.sleep(1000);//模拟1秒延迟
}catch(InterruptedExceptione){
Thread.currentThread().interrupt();
}

returnnumber*number;
}
}

I/O密集型任务

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

读取或写入大文件到磁盘(例如,保存视频文件、加载数据库)

通过网络下载或上传文件(例如,浏览网页、观看流媒体视频)

发送和接收电子邮件

运行Web服务器或其他网络服务

执行数据库查询

Web服务器处理传入请求

优化I/O密集型任务的方式

在内存中缓存频繁访问的数据,以减少重复I/O操作的需要。

负载均衡,将I/O密集型任务分配到多个线程或进程中,以高效处理并发I/O操作。

使用SSD,固态硬盘(SSDs)相较于传统硬盘(HDDs)可以显著加快I/O操作。

使用高效数据结构,如哈希表和B树,以减少所需的I/O操作次数。

避免不必要的文件操作,例如多次打开和关闭文件。

4.在两种任务中确定线程数

确定CPU密集型任务的线程数量

对于CPU密集型任务,要最大化CPU利用率,而不让系统因线程过多而超负荷,防止过度的上下文切换。一个常见方法是使用可用CPU核心数量。假设需开发一个视频处理应用程序。视频编码是一项CPU密集型任务,需要应用复杂算法来压缩视频文件。有一个多核CPU可用。

计算可用CPU核心:使用Runtime.getRuntime().availableProcessors()确定可用的CPU核心数量。假设有8个核心。

创建线程池:创建一个大小接近或略少于可用CPU核心数量的线程池。在这种情况下,您可以选择6或7个线程,以便为其他任务和系统进程留出一些CPU容量。

importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;

publicclassVideoEncodingApp{
publicstaticvoidmain(String[]args){
intavailableCores=Runtime.getRuntime().availableProcessors();
intnumberOfThreads=Math.max(availableCores-1,1);//根据需要进行调整

ExecutorServicethreadPool=Executors.newFixedThreadPool(numberOfThreads);

//将视频编码任务提交到线程池。
for(inti=0;i< 10; i++) {
            threadPool.execute(() ->{
encodeVideo();//模拟视频编码任务
});
}

threadPool.shutdown();
}

privatestaticvoidencodeVideo(){
//模拟视频编码(CPU 密集型)任务。
//这里进行复杂计算和压缩算法。
}
}

确定I/O密集型任务的线程数量

对于I/O密集型任务,最佳线程数通常由I/O操作的性质和预期延迟决定。您希望拥有足够的线程以保持I/O设备繁忙而不使其过载。理想的数量不必等于CPU核心的数量。考虑构建一个网页爬虫,下载网页并提取信息。这涉及HTTP请求,这是因网络延迟引起的I/O密集型任务,可从如下两方面进行分析。

分析I/O延迟:估计预期的I/O延迟,这依赖于网络或存储。例如,如果每个 HTTP 请求大约需要500毫秒完成,您可能需要考虑I/O操作中的一些重叠。

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

以下是网页爬虫的示例代码。

importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;

publicclassWebPageCrawler{
publicstaticvoidmain(String[]args){
intexpectedIOLatency=500;//估计的I/O延迟(毫秒)
intnumberOfThreads=4;//根据预期延迟和系统能力进行调整

ExecutorServicethreadPool=Executors.newFixedThreadPool(numberOfThreads);

//要抓取的 URL 列表。
String[]urlsToCrawl={
"https://example.com",
"https://google.com",
"https://github.com",
//在这里添加更多URL
};

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

threadPool.shutdown();
}

privatestaticvoidcrawlWebPage(Stringurl,intexpectedIOLatency){
//模拟网页抓取(I/O 密集型)任务。
//执行 HTTP 请求并处理页面内容。
try{
Thread.sleep(expectedIOLatency);//模拟I/O延迟
}catch(InterruptedExceptione){
Thread.currentThread().interrupt();
}
}
}

5.总结公式

确定线程池大小的公式可以写成如下形式。

线程数=可用核心数*目标CPU利用率*(1+等待时间/服务时间)

该公式各部分的详细解释如下。

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

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

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

服务时间:这是线程执行计算的时间。

阻塞系数:这是等待时间与服务时间的比率。它是衡量线程在 I/O 操作完成之前等待的时间相对于执行计算时间的比例。

示例使用

假设有一个具有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学研大本营公众号

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10918

    浏览量

    213172
  • 操作系统
    +关注

    关注

    37

    文章

    6902

    浏览量

    123829
  • JAVA
    +关注

    关注

    19

    文章

    2977

    浏览量

    105240
  • 线程池
    +关注

    关注

    0

    文章

    57

    浏览量

    6898

原文标题:如何正确判断Java线程池的大小

文章出处:【微信号:FPGA研究院,微信公众号:FPGA研究院】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    相关推荐

    Java的线程包括哪些

    线程是用来统一管理线程的, Java 创建和销毁线程都是一件消耗资源的事情,线程可以重复使用线程,不再频繁的创建、销毁线程。 初识
    的头像 发表于 10-11 15:33 868次阅读
    <b class='flag-5'>Java</b><b class='flag-5'>中</b>的线程<b class='flag-5'>池</b>包括哪些

    USB八口MODEM USB八口MODEM USB八口短信MODEM Q2406A

    MODEM WAVECOM Q24Plus短信猫二次开发专家(DELPHI/ASP/PB/VB/VC/C#/JAVA/ 中间件数据库接口)16口短信MODEM 八口MODEM U
    发表于 10-30 18:08

    USB八口MODEM USB八口MODEM

    MODEM WAVECOM Q24Plus短信猫二次开发专家(DELPHI/ASP/PB/VB/VC/C#/JAVA/ 中间件数据库接口)16口短信MODEM 八口MODEM U
    发表于 06-28 15:28

    如何确定SPI FlashFPGA配置的大小

    伙计们,我的电路板提供了FPGA处于硬复位状态时读取SPI闪存的能力(这种能力独立于FPGA;可以说是“侧读”)。假设我有一个具有有效FPGA配置的SPI Flash。如何确定SPI Flash
    发表于 06-09 13:28

    内存可以调节内存的大小

    嵌入式–内存直接上代码,自己体会。嵌入式设备,一般keil提供的堆很小,一般都不使用。使用内存,自己可以调节内存大小。头文件 malloc.h#ifndef __MALLOC_H#define
    发表于 12-17 07:00

    关于RT-Thread内存管理的内存简析

    连接起来。分配内存块。在用户申请内存块时,从空闲链表取出第一个内存块给申请者。内存工作机制如下图所示。注意:内存一旦创建并初始化完成后,其内部的内存块大小就固定了,不能再做调整。
    发表于 04-06 17:02

    java 常量静态变量详解

    Java的常量,实际上分为两种形态:静态常量和运行时常量。 所谓静态常量,即*.cla
    发表于 09-27 13:01 10次下载
    <b class='flag-5'>java</b> 常量<b class='flag-5'>池</b>静态变量详解

    数据库连接的设置怎么确定大小

    数据库连接的配置是开发者们常常搞出坑的地方,配置数据库连接时,有几个可以说是和直觉背道而驰的原则需要明确。
    的头像 发表于 05-04 14:23 2811次阅读
    数据库连接<b class='flag-5'>池</b>的设置怎么<b class='flag-5'>确定</b><b class='flag-5'>大小</b>

    HK-MR340系列:用于确定肿瘤的大小和位置

    医学治疗过程想要确定肿瘤的大小和位置,但苦于缺乏互补的线性位置反馈?同时又想要在检测过程
    的头像 发表于 08-31 16:48 918次阅读
    HK-MR340系列:用于<b class='flag-5'>确定</b>肿瘤的<b class='flag-5'>大小</b>和位置

    Java线程核心原理

    看过Java线程源码的小伙伴都知道,Java线程池中最核心的类就是ThreadPoolExecutor,
    的头像 发表于 04-21 10:24 920次阅读

    【虹科新品】HK-MR340系列:用于确定肿瘤的大小和位置

    HK-MR340系列用于确定肿瘤的大小和位置简介在医学治疗过程想要确定肿瘤的大小和位置,但苦于缺乏互补的
    的头像 发表于 09-01 15:13 654次阅读
    【虹科新品】HK-MR340系列:用于<b class='flag-5'>确定</b>肿瘤的<b class='flag-5'>大小</b>和位置

    JSF业务线程大小配置用例说明

    JSF 业务线程使用 JDK 的线程技术,缺省情况下采用 Cached 模式(核心线程数 20,最大线程数 200)。此外,还提供了 Fixed 固定线程大小的模式,两种模式均可设置请求队列
    的头像 发表于 09-19 11:15 936次阅读

    化技术的应用实践

    作为一名Java开发人员,化技术或多或少在业务代码中使用。常见的包括线程、连接等。也是因为Java语言超级丰富的基建,基本上这些
    的头像 发表于 11-24 10:22 579次阅读
    <b class='flag-5'>池</b>化技术的应用实践

    线程的运转流程图 化技术实践案例解析

    作为一名Java开发人员,化技术或多或少在业务代码中使用。常见的包括线程、连接等。也是因为Java语言超级丰富的基建,基本上这些
    的头像 发表于 11-24 10:22 520次阅读
    线程<b class='flag-5'>池</b>的运转流程图 <b class='flag-5'>池</b>化技术实践案例解析

    平衡电阻大小如何确定,平衡电阻阻值大小的选择原则

    电子电路设计,平衡电阻(Balancing Resistor)扮演着至关重要的角色。它不仅有助于实现电路的平衡和稳定性,还能减少噪声和干扰,提高信号质量。然而,平衡电阻的大小和阻值选择并非随意
    的头像 发表于 01-30 14:33 230次阅读