需要写一个程序,对某个目录下7000多个文本文件读取,整个目录大小为100多G,文本文件一行为一条数据。 要求尽快读取完数据。
主机配置:24颗逻辑CPU,40G内存
初步设计是:采用24个线程的固定线程池,
目录读取任务:1个,负责目录内文件名的读取,读取后交给文件读取线程
文件读取任务: 11个,负责读取文件内容,即同时读取11个文件,每个文件读取出一条时,交给行处理线程。
行处理任务:12个,仅做输出日志(每处理1000行输出一条日志)。
每一行数据生成一个Runable,丢线线程池。
每一个任务对应一个Runnabe,存入线程池, 由于行数据太多,生成的Runnabe在线程池队列中过多,从而产生抢占其它线程,即行处理线程数可能会多于12个,其它线程没有时间片可用,所以限制了任务数量,即在线程池中:
运行中+队列中:目录读取任务 只有1个。(总共也只有一个)
文件读取任务 只有11个。(总共==文件数)
行处理任务只有12个。(总共==所有文件行数)
程序中控制只有任务数小于应有任务数时,才调用线程池execute(新任务),否则阻塞。
问题1:
测试时发现,内存一直增加,越来越高。
分析认为: 由于行处理任务,每处理1000行输出一条日志,运行时间太短,基本上0ms,任务退出行,当前线程发现队列中无可用任务,阻塞,当前行处理任务小于6,则添加任务线程变为运行状态,添加行处理任务到队列,线程池中阻塞的线程运行,0ms后再次阻塞。
简单说:就是由于任务运行时间太短,任务量太多,线程不断的在 阻塞-->运行-->阻塞-->运行状态间切换,造成内存消耗过多。
改造方案:
目录读取任务 只有1个。(总共也只有一个)
文件读取任务 只有11个。(共享一个文件队列,处理完当前文件后,从队列中取出下一个文件处理,即线程不阻塞,在持续的处理)
行处理任务只有12个。(共享一个行数据队列,处理完当前行后,从队列中取出下一个行数据,即线程不阻塞,在持续的处理)
结果:内存到达一个值后不再增加。
问题2:
发现程序刚开始时运行很快,后来,越来越慢
用top命令(top后按1)查看时各个cpu 运行情况:
Tasks: 469 total, 2 running, 466 sleeping, 0 stopped, 1 zombie
Cpu0 : 39.4%us, 0.7%sy, 0.0%ni, 0.0%id, 59.9%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu1 : 2.7%us, 0.3%sy, 0.0%ni, 54.2%id, 42.9%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu2 : 1.3%us, 0.3%sy, 0.0%ni, 87.7%id, 10.6%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu3 : 0.7%us, 0.0%sy, 0.0%ni, 95.7%id, 3.7%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu4 : 0.3%us, 0.0%sy, 0.0%ni, 97.4%id, 2.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu5 : 0.7%us, 0.0%sy, 0.0%ni, 94.4%id, 5.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu6 : 1.3%us, 0.3%sy, 0.0%ni, 84.4%id, 13.9%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu7 : 1.3%us, 0.0%sy, 0.0%ni, 90.0%id, 8.6%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu8 : 0.3%us, 0.3%sy, 0.0%ni, 97.0%id, 2.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu9 : 0.7%us, 0.3%sy, 0.0%ni, 93.7%id, 5.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu10 : 0.3%us, 0.0%sy, 0.0%ni, 97.4%id, 2.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu11 : 34.0%us, 0.7%sy, 0.0%ni, 1.0%id, 64.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu12 : 8.9%us, 0.3%sy, 0.0%ni, 24.2%id, 66.6%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu13 : 0.7%us, 0.3%sy, 0.0%ni, 96.7%id, 2.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu14 : 0.3%us, 0.0%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu15 : 0.3%us, 0.3%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu16 : 1.3%us, 0.3%sy, 0.0%ni, 89.4%id, 8.6%wa, 0.0%hi, 0.3%si, 0.0%st
Cpu17 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu18 : 1.0%us, 0.7%sy, 0.0%ni, 95.0%id, 3.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu19 : 0.0%us, 0.3%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu20 : 0.3%us, 0.3%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu21 : 0.7%us, 0.0%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu22 : 0.3%us, 0.3%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu23 : 7.3%us, 0.3%sy, 0.0%ni, 0.0%id, 92.4%wa, 0.0%hi, 0.0%si, 0.0%st
发现cpu等待IO时间过长,
找到运行的java进程,用jstack命令查看:
jstack 进程号, 发现有11个线程都在文件读取状态:
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.FileDispatcher.read0(Native Method)
at sun.nio.ch.FileDispatcher.read(FileDispatcher.java:26)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:198)
at sun.nio.ch.IOUtil.read(IOUtil.java:171)
at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:144)
- locked <0x00000007b47a0d20> (a java.lang.Object)
再用iostat命令查看IO情况:(iostat -x 1 100)每陋一条显示一次io信息
avg-cpu: %user %nice %system %iowait %steal %idle
4.34 0.00 0.21 17.28 0.00 78.17
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sd1 0.00 7.00 0.00 9.00 0.00 128.00 14.22 0.15 16.78 4.11 3.70
sd2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sd3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sd4 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sd5 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sd6 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sd7 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sd8 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sd9 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sd10 101.00 0.00 179.00 0.00 71176.00 0.00 397.63 21.37 76.37 5.59 100.00
%util:(上面命令每隔1秒显示一次)1秒内IO时间有多少,即io时间占总时间比例,如100%,即一直在读取硬盘文件。
所有需要读取的文件都存在sd10上,即11条线程同时读一块硬盘上数据,造成io过高。
后一来把文件读取任务改成1个线程,整个程序运行时间与11条线程差不多。
后来在网上查,说持续读硬盘写硬盘,尽量用一条线程。
如果需要提高处理速度,可以把文件分布在不同的硬盘上,多条线程读取。 跟Cpu类似,密集计算型线程数要与逻辑Cpu数相等。
另外:感觉可以查看io情况适当的增加线程数,如一条线程硬盘 %utils= 20% ,是否可以增加1条线程读取呢,未测。
问题三:
线程数量问题
1个文件读取线程供应不上12条行处理线程的速度,造成 12条行线程有一半在阻塞状态,浪费线程,(原因行处理线程任务时间太短),通过查看线程待处理队列(12个行处理线程共享一个行数据队列,从中取数据,行处理数据队列大小时常为0),适当调整线程数。
问题四:
任务拆分
当查看一个任务的CPU的占有率为 90%以上时,是否考虑任务拆分成多个任务,则多个CPU同时处理,以增加处理速度
相关推荐
Java读取大文件的处理 Java读取大文件的处理是Java编程中的一项重要技术,特别是在处理大文件时需要注意性能和响应速度。下面我们将对Java读取大文件的处理技术进行详细的介绍。 标题解释 Java读取大文件的处理是...
### Java读取大文件技术详解 #### 一、引言 在Java开发中,处理大文件是一项常见的任务,尤其在数据分析、日志处理等领域尤为重要。本文将深入探讨如何使用Java来高效地读取大文件,并提供一个具体的示例代码进行...
为了高效地处理这类问题,我们可以利用Java的`java.nio`包中的BufferedReader和FileChannel等类,实现按行读取大文件,并将其内容解析后存储到数据库中。本文将详细讲解这一过程。 首先,我们需要了解`java.nio`包...
### Java读取文件方法大全:读取File流等技术 在Java中,读取文件是一项基本且重要的操作,它可以通过多种方式实现,如字节流、字符流和基于行的读取。下面将详细介绍这些方法: #### 字节级读取:`...
通过这种方法,Java程序能够高效地处理大文件,避免一次性加载整个文件导致的内存问题,同时通过缓冲区和内存映射文件优化了读取性能。这种方法尤其适用于处理非常大的文件,如日志文件或大数据分析中的文件。
`mpxj`是一个开源的Java库,专门用于读取和写入Microsoft Project文件,包括MPP格式。这个库提供了丰富的API,使得开发者可以在Java应用中方便地操作这些项目管理文件。 首先,让我们详细了解一下如何在Java中使用`...
以上就是使用Java读取TXT文件并将其内容存入数据库的基本步骤。根据实际需求,可能还需要处理异常、优化性能(如批处理SQL)、验证数据格式等细节问题。在处理大规模数据时,考虑使用多线程或者流式处理来提高效率。
Java读取CSV例子 Java读取CSV例子是通过使用javacsv-2.0.jar包来实现的。javacsv-2.0.jar包提供了CsvReader和CsvWriter两个类,分别用于读取和写入CSV文件。在本例子中,我们将展示如何使用javacsv-2.0.jar包来...
"Java读取Zip文件和文件内容" Java无需解压直接读取Zip文件和文件内容是Java语言中的一种常见操作,通过使用java.util.zip包中的ZipFile、ZipInputStream和ZipEntry类,我们可以轻松地读取Zip文件和文件内容。下面...
### Java读取SHP文件及DBF属性的关键技术解析 #### 概述 在地理信息系统(GIS)领域,Shapefile是一种常见的矢量数据格式,用于存储地理位置信息及相关属性数据。一个完整的Shapefile由多个文件组成,包括.shp、....
完美解决java读取大文件内存溢出的问题 在Java中读取大文件时,内存溢出是一个常见的问题。传统方式的读取文件方式会将整个文件读取到内存中,导致程序抛出OutOfMemoryError异常。但是,我们可以通过行迭代方式来...
在Java编程中,读取配置文件是常见的任务,特别是在开发需要灵活配置的系统时。配置文件通常用于存储应用程序的设置,如数据库连接信息、服务器端口、第三方服务的API密钥等,这些信息可能需要根据不同的环境或需求...
在Java编程环境中,读取PDF文件是一项常见的任务,特别是在开发桌面应用或需要处理PDF文档内容时。本篇文章将深入探讨如何使用Java来读取PDF文件,并将其内容展示在一个由JFrame和JPanel构建的GUI窗口中。 首先,...
本文将深入探讨如何使用Java读取PDF文件的属性,如作者、标题等元数据,以及相关的知识点。 ### 一、Java读取PDF文件属性的技术背景 在Java中读取PDF文件属性主要依赖于开源库PDFBox。PDFBox是一个用于处理PDF文档...
1. 高效:Java 读取 Excel 文件可以快速读取大量数据。 2. 灵活:Java 读取 Excel 文件可以对数据进行灵活的处理和分析。 3. 可靠:Java 读取 Excel 文件可以确保数据的安全和可靠。 Java 读取 Excel 文件的困难点 ...
要实现Java读取局域网共享文件,主要涉及以下步骤和知识点: 1. **网络路径表示**:Java中,网络路径通常以UNC(Universal Naming Convention)格式表示,形如`\\hostname\shared_directory\file.txt`。这里`...
很好的解决大文件读取中需要耗费大量时间的问题,三种方法各异,对不同的文本文件都能较好解决。
标题"java读取excel之xlsl超大文件"所涉及的核心知识点是优化大量数据的读取策略。在Java中,可以使用Apache POI的SXSSF API(Streaming Usermodel API)来实现这种优化。与常规的XSSF API不同,SXSSF允许我们以流的...
本教程将深入讲解如何使用Java读取JSON文件,并将其内容转化为Map以便进行取值操作。 首先,我们需要引入处理JSON的库。Java标准库并不直接支持JSON操作,所以我们通常会使用第三方库,如`org.json`或`...
要读取XML文件,Java提供了多种方法。以下是四种常用的方法,每种都有其适用场景和特点: 1. **DOM解析器(Document Object Model)** DOM解析器将整个XML文件加载到内存中,创建一个树形结构,允许我们通过节点...