`

JAVA 读取大量文件

 
阅读更多

需要写一个程序,对某个目录下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同时处理,以增加处理速度

 

 

 

 

 

 

 

 

         

 

             

             

           

 

            

            

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics