小弟刚学java,,但对java的多线程以及其他一些东西不太熟悉,尤其是1.5之后似乎原来的并发机制已被更优的方法代替了,更让人不知用什么才好。现在正在做一个实习项目,要求如下:
1,有10个文本文件,每个220M左右,每个文件记录大约2千万条,每条记录格式相同,包括每条记录的原始时间戳,但每条记录中也有些是无用的信息,处理时去掉。
2,要产生用户指定的记录条数,并对其做相应的处理(细节略)
3,将产生的记录放入一个队列中,生产者在队列满时阻塞,消费者在队列空时等待我的问题是:
1,想用多线程来提高处理的效率,想请问大家的是,有没有办法用多个线程读取一个文件的内容来提高速度,比如线程1读取文件的第一行,线程2同时读取第二行如果这点做不到,那是不是应该把读文件和处理记录放在不同的线程中来提高效率?但是处理记录的方法必须依赖于读到的每一行的内容,如何在不同的线程中实现呢?
2,老师的要求是用一个指定大小的队列来存放这些记录,并根据每条记录的原始时间戳来生成新的时间戳(只是为了保持原有的先后顺序),然后将它们按时间戳排序,等待用户来拿,而用户取得的记录将根据记录的时间戳来决定何时发送给其他人(这点不用我实现)。我本来想用PriorityQueue来实现,但是如果有多个线程在进行生产和获取记录,那么根本保证不了用户拿到所有的记录时是按时间戳排序的,更别说根据时间顺序发送出去了老师说可以让用户自己实现一个sortedSet进行排序,那如果这家伙要生成2亿条记录,估计他得准备个2G内存来存储并排序这些数据了有什么办法可以减少内存开销又获得比较好的性能呢?
3,上面那个queue的问题,好像不但解决不了时间戳的先后顺序,而且多线程在这个queue上根本没用武之地吧?只能一头进一头出,如果空了或者满了,再多的线程都得等着,加上同步的开销,好像还不如单线程来的快。我是否应该改用其他数据结构比如list什么的 加上多线程?
4,最头痛的一点,这个程序到时要部署到多个网络节点上去的,但是老师又说,原始数据太庞大,不想就这样放到每个节点上去那如果不放上去怎么读取到足够的数据呢??用数据库会不会节省些空间开销?或者需要搞分布式?花了好多时间了,多线程的东西还是一知半解,写程序写到郁闷死,最后发现照我现在的写法多线程还不如单线程快更要命的是结构上还有很多东西没定下来,根本没法往下写了,各位大侠救救我吧!

解决方案 »

  1.   

    谢谢楼上的,是不是new多个RandomAccessFile对象,然后每个对象读取的时候做相应的seek?但是线程之间是否要互相通信同步啊?否则怎么知道该seek多少咧?这样的开销会不会比较大?还是说少开点线程比较好?同步怎么做?用pipe通信还是用个共享变量比较好呢?
      

  2.   

    大文件读写用 java.nio api
      

  3.   

    那请问怎么用nio呢?看到都是一堆channel实在不懂啊,是map到内存吗?我这个文件是应该是适合按行读取的,好像没找到nio里哪个可以readLine的类啊
      

  4.   

    关注, 一个文本做到几百M也够狠, 就是记日志的话, 也可参考weblogic里设置比方说5M大小一个文本LOG, 文件名的序列从小到大排下去.
      

  5.   

    2000万条的数据,有10个,就算每次处理一个,而且是要使用集合类,
    除非你把堆内存开到1G,否则根本就不可能完成!因为集合类比较耗内
    存。再有在单CPU环境下,使用多线程读取文件,根本就不会提高多少效率。
      

  6.   

    可以试试在Queue中放多少条数据,heap就溢出了。
      

  7.   

    在32位结构下,JVM能使用的内存最大不可能超过2G,实际上使用java命令的-Xmx参数可以给到最大的也只有1G多一些。要提高效率和降低内存占用,首先需要舍弃使用Java中的集合类来存储数据。
      

  8.   


    首先,做多线程就一定要用jdk1.5以后的。因为新的多线程机制要比之前的版本好用很多,本质的区别。另外你说的,多线程读文本,关键在于,不要一行一行的读数据. 设定一个缓冲区,每次都一次性读进来.我觉得线程操作应该是面向你的缓冲区的,而不是直接面向文本的。至于你说的排序什么的,我是真有点困惑了,没看懂你的描述。另外,使用多线程不等于高效率。我想你还是不要先入为主的就说要用多线程。还是仔细划分一下业务模块。那些原子级的模块,你就可以用独立线程去处理。还有如果你面向的用户是并发访问的,那么也可以用多线程处理。介绍一本书给你---《并发编程实践》 电子工业出版社
      

  9.   

    谢谢楼上各位的回答!有个地方我写错了,具体的记录是每个文件36万多条,总共3600多万条,但是文件的大小是正确的,200多M。经过我预处理后删除了一些无用的信息,,但每个也还是有160多M。机器是双核的,但是对于多线程读文件的想法我已经放弃了,似乎硬盘将是瓶颈。不过我倒是很想知道是否真的能达到我一开始那种天真的想法:每个线程读同时一行,如果硬盘只有一个磁头来回跑的话是不是根本没有可能多个线程同时读一个文件的不同行呢?
    关于什么时候堆内存溢出,eclipse的话JVM好像设不到1024M,反正一开始我没改eclipse默认的JVM内存大小,写的程序是边读边处理边放进Queue里面,100万条的时候还OK,后来想改用多个线程读,结果不知道是不是因为我程序写的垃圾,把eclipse的JVM改到768M,还是在50万条的时候溢出了,不用eclipse,把JVM改到1024M,100万条OK,但速度明显比我原来的单线程程序慢了很多我现在在学习用nio读大文件,有个问题,160多M的文件是不是肯定要map文件到内存?这样的话内存就更不够用了,那是不是最好的办法就是把读文件和处理的逻辑分在2个线程里,再加个合适大小的buffer,这样相当于消费者和生产者了。但关键是最后的那个对时间戳排序,这时间戳还不是真实的时间戳,是为了保证原有的时间戳的先后顺序而生成的,太不现实了除非写进数据库再进行排序吧?否则内存里哪来那么多空间把所有的东西存起来进行排序咧??
      

  10.   

    另外,我看到有个nio的例子,里面有指定MappedByteBuffer时如下语句: 
    static int length = 0x8FFFFFF; // 128 Mb 
    8FFFFFF怎么会是128Mb呢?就算从0算起,length也应该是7FFFFFF吧?
      

  11.   

    请问楼上什么地方不清楚呢?我现在最大的问题就是如何有效的处理大文件。
    好像可以用nio进行文件映射,但一次映射整个160M的文件会不会大了点?但如果只映射部分文件,比如mappedbytebuffer大小设16M,因为我处理的时候需要按行来读记录,按换行符读缓存的话,肯定每次会有些剩下的,到时是不是还得把channel的position移回到上一行的结尾,保证能读取完整的一行,然后继续映射啊??
      

  12.   

    楼主,不好意思,我不知道你看到了我在8楼给你的回复了吗?曾经在CSDN回复过一个关于两个2G文件比较不同的问题,貌似与你的问题有异曲同工之处,你可以去参考一下:http://topic.csdn.net/u/20071119/23/8af044b3-8c28-4db0-8303-4a8679f2356d.htmlPS:我回复的代码在41~43楼。
      

  13.   

    bao110908朋友,你的代码我正在研究学习中,因为是初学所以看的可能慢点,不过在此先向你以及楼上各位热心的朋友表示感谢。
    因为时差关系,还有些问题等上课回来再请教你。。
    PS:对nio的内存映射的使用还是不太明白,是不是如果只将文件的一部分映射到内存,比如映射到64M大小的buffer,下次我还想接着上次的地方继续读文件,那就必须在文件当前位置重新开个通道?
      

  14.   

    还有,bao110908朋友,请问你是C爱好者吗?看到你都用printf做输出嘛:D
      

  15.   

    关于堆内存占用的问题,如果不用内置的集合类,那应该用什么呢?我这里不是要比较各行的内容,而是要确实的获得每行的内容并进行处理,转成int或其他类型行不通的吧?还有,如果map到内存去读,速度是快了,但是获得每行的内容变得更困难了,好像会得不偿失啊。。
      

  16.   

    [size=30px]to: ooooeee[/size]还有,bao110908朋友,请问你是C爱好者吗?看到你都用printf做输出嘛:D
    _____________________________________________________________嘿嘿,我不是C语言的爱好者,对C语言只知皮毛,之所以用printf,主要是格式
    化输出更为方便的啦~~3600多万条,就算一个文件也有36万条,近160多M,全部塞进Map中根本就是不
    可能的!就算塞进去内存得开得很大。你可能还没有完全理解我的意思,像上次那个2G文件的那个,人家一上来就把格
    式、需求都说得很清楚。像你的这个,我到现在为止都不知道你的文件格式是什
    么?具体要做些什么?(细节竟然略掉了)
    像这种大文件,只能根据格式、需求
    来找出算法的。真的很抱歉,根本不能帮你想到什么实质性的办法!
      

  17.   

    不好意思,我以为那些没太大关系呢具体是这样的,老师要求做一个pub/sub系统的测试用API,以一个网站发布的搜索数据做源数据,根据指定参数(具体的有具体的query条数,或不同的user的个数,或者提交过M到N条query的user所提交的所有query条数)生成subscription,然后由pub/sub系统发布出去。源数据格式如下:
    UID  QUERYCONTENT  DATE 
    源数据以UID排序,但是老师要求最后按照DATE的先后顺序生成相应的timestamp,并在queue中按timestamp进行排序,然后由client提取queue中的subscription,按timestamp规定的时间间隔发布不知道这样描述是否清楚原描述是英文的,大概就这意思了。。
      

  18.   

    不好意思,昨天没看到 :)1,最后按照DATE的先后顺序生成相应的timestamp
    不明白,DATE和Timestamp有些什么区别?两个都是时间戳之间的东西,能否举个例子?2,并在queue中按timestamp进行排序
    不明白,为什么要在Queue中排序呢?3,可能我比较笨不能完全了解老外的想法,我还是没有看懂具体要做些什么,感觉越看越糊涂了……如果不进行数据的转换,采用占用容量更小的数据类型来存储的话,采用集合类,
    这个任务近乎是不可能完成的(限于内存)。如果有必要把数据导入到数据库中好像更为合适一些。
      

  19.   

    谢谢bao110908大侠一直不厌其烦的回帖,关于时间戳的问题是这样的:
    1,原始数据格式如下:
    142 broadway.vera.org 2006-04-08 08:39:302,这里的时间是未处理前的时间戳,但是老师要求的是在用这些数据生成subscription的时候,客户可以指定所要生成sub的条数和时间间隔。比如说客户指定生成1000条sub,他们的时间跨度是10分钟,而程序从原始文件读到的记录可能时间跨度是1个月,那我就需要在保持原始记录的时间先后顺序的同时做相应的时间间隔压缩,把跨度一个月的记录的时间戳压缩到10分钟内,然后在queue中根据时间戳的先后顺序进行排序,这样用户在提取sub的时候,可以根据sub的时间戳决定何时发布这些sub,比如提取到的第一个sub的时间戳是0,那么就立即发布,第二个时间戳是3000,那么就3秒后发布,反正就是保证在10分钟内把这些sub发布出去就对了是否将原始数据放到数据库比较好点?老师说如果我要用数据库,那就用轻量级的容易打成jar包和程序一起发布的,比如tinySQL之类的,不知道有没有人用这个小玩意儿?
      

  20.   

    再请教一个问题,如果用一个线程读映射到内存中的文件,这个线程将读到的每行内容分别放入N个buffer里,然后另外开辟N个线程,每个线程分别从不同的一个buffer里获得内容进行处理,并放入最终的queue中,这样速度能提高些吗?但是这样好像同步有些困难,我写的程序,虽然该生成的sub都生成了,但是有些处理线程会因为buffer空了一直wait,因为到最后别的线程都结束了,没人notify它了是否需要个别的对象来进行同步??多线程实在太郁闷了。。