前言
  是五一假期第,按理应该是快乐玩耍的日子,但是作为一个北漂到京师的开发人员,实在难想出去那玩耍。好玩的地方比较远,近处又感觉没意思。于是乎,闲着写篇文章,总结下昨天写的程序吧。
  昨天下午朋友跟我聊起,他说有个需求,需要把上G的txt文件读取写入到数据库。用普通的io结果自然是OOM了,所以果断用NIO技术。为了提高速度,自然还得用上多线程技术。
  接下来介绍一下实现思路以及相关的知识点。
  内容
  一、对文件分区
  为了充分利用多线程读取,需要把文件划分成多个区域,供每个线程读取。那么需要有一个算法来计算出每个线程读取的开始位置和结束位置。那么首先根据配置的线程数和文件的总长度计,算出每个线程平均分配的读取长度。但是有一点,由于文件是纯文本文件,必须按行来处理,如果分割点在某一行中间,那么这一行数据会被分成两部分,分别由两个线程同时处理,这种情况是不能出现的。所以各个区域的结束点上的字符必须是换行符。第一个区域的开始位置是0,结束位置首先设为(文件长度/线程数),如果结束点位置不是换行符,只能加1,直到是换行符位置。第一个区域的结束位置有了,自然我们能求出第二个区域的开始位置了,同理根据上边算法求出第二个区域的结束位置,然后依次类推第三个、第四个......
  上边的算法中,第一个区域的结束位置定了,才能有第二个区域的开始位置,第二个区域的结束位置定了,才能有第三个区域的开始位置,依次这么下去。照这种规律,自然地想到的是用递归来解决。(详情看源码)
  二、内存文件映射
  简单说一下内存文件映射:
  内存文件映射,简单地说是将文件映射到内存的某个地址上。
  要理解内存文件映射,首先得明白普通方式读取文件的流程:
  首先内存空间分为内核空间和用户空间,在应用程序读取文件时,底层会发起系统调用,由系统调用将数据先读入到内核空间,然后再将数据拷贝到应用程序的用户空间供应用程序使用。这个过程多了一个从内核空间到用户空间拷贝的过程。
  如果使用内存文件映射,文件会被映射到物理内存的某个地址上(不是数据加载到内存),此时应用程序读取文件的地址是一个内存地址,而这个内存地址会被映射到了前面说到的物理内存的地址上。应用程序发起读之后,如果数据没有加载,系统调用会负责把数据从文件加载到这块物理地址。应用程序便可以读取到文件的数据。省去了数据从内核空间到用户空间的拷贝过程。所以速度上也会有所提高。
  在我的读取大文件的实现中,是用了Java的内存映射API,这样我们可以在要读取某个地址时再将内容加载到内存。不需要一下子全部将内容加载进来。
  总结
  以上是我主要用到的思路和一些技术点吧。可能存在某些表达不清楚的地方,望大神勿喷^_^
  具体实现请参考代码吧,这里是代码的地址(Java读取大文件)