2011-04-01 63 views
2

我想用不同的块读取日志文件以使其成为多线程。该应用程序将运行在具有多个硬盘的服务器端环境中。 读入块后,应用程序将处理每个块的每行。Java - 按块读取文本文件

我已经使用bufferedreader完成了每个文件行的读取操作,并且可以使用RandomAccessFile和MappedByteBuffer组合我的文件块,但将这两者结合起来并不容易。

问题是块正在切入我的块的最后一行。我从来没有完成我的块的最后一行,因此处理这最后的日志行是不可能的。我试图找到一种方法来将我的文件切割成可变长度的块,以保证行结束。

有没有人有这样做的代码?

+1

这似乎不太可能确实是在读多线程一个单一的文件会比单个线程读取速度更快。磁盘在顺序访问方面非常出色,在随机访问方面则较少。如果瓶颈在处理中而不是IO(再次,看起来不太可能),那么读取一个线程中的所有数据,并将阻塞移交给要处理的工作线程。我建议你将并行性限制为一次处理多个文件,每个文件只有一个线程。 – 2011-04-01 10:03:03

回答

8

在开始处理块之前,您可以在文件中找到位于边界的偏移量。通过将文件大小除以块号开始偏移,直到找到一条线边界。然后将这些偏移量送入您的多线程文件处理器。下面是一个使用可用的处理器数块数的完整的例子:

import java.io.File; 
import java.io.IOException; 
import java.io.RandomAccessFile; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class ReadFileByChunks { 
    public static void main(String[] args) throws IOException { 
     int chunks = Runtime.getRuntime().availableProcessors(); 
     long[] offsets = new long[chunks]; 
     File file = new File("your.file"); 

     // determine line boundaries for number of chunks 
     RandomAccessFile raf = new RandomAccessFile(file, "r"); 
     for (int i = 1; i < chunks; i++) { 
      raf.seek(i * file.length()/chunks); 

      while (true) { 
       int read = raf.read(); 
       if (read == '\n' || read == -1) { 
        break; 
       } 
      } 

      offsets[i] = raf.getFilePointer(); 
     } 
     raf.close(); 

     // process each chunk using a thread for each one 
     ExecutorService service = Executors.newFixedThreadPool(chunks); 
     for (int i = 0; i < chunks; i++) { 
      long start = offsets[i]; 
      long end = i < chunks - 1 ? offsets[i + 1] : file.length(); 
      service.execute(new FileProcessor(file, start, end)); 
     } 
     service.shutdown(); 
    } 

    static class FileProcessor implements Runnable { 
     private final File file; 
     private final long start; 
     private final long end; 

     public FileProcessor(File file, long start, long end) { 
      this.file = file; 
      this.start = start; 
      this.end = end; 
     } 

     public void run() { 
      try { 
       RandomAccessFile raf = new RandomAccessFile(file, "r"); 
       raf.seek(start); 

       while (raf.getFilePointer() < end) { 
        String line = raf.readLine(); 
        if (line == null) { 
         continue; 
        } 

        // do what you need per line here 
        System.out.println(line); 
       } 

       raf.close(); 
      } catch (IOException e) { 
       // deal with exception 
      } 
     } 
    } 
} 
+0

非常感谢。这就是我需要的! – Yoni 2011-04-05 03:50:53

+0

没问题。你可能想接受答案:)当你提出更多问题时,它会有所帮助,人们喜欢看到你接受答案。 – WhiteFang34 2011-04-05 04:08:12

0

你需要让你的块重叠。如果没有行比一个块长,那么一个块重叠就足够了。 你确定你需要多线程版本吗? gnu grep的性能不够好吗?

gnu grep的实现已经解决了跨越块边界的行的问题。如果你不打扰GNU许可证,你可以从那里借用想法和代码。这是一个非常有效的单线程实现。

+0

我被分配到这个项目中,并且它必须是多线程的,因为会有多个文件(大于500mb)在大范围内共享,并且所有内容都必须尽可能快。 – Yoni 2011-04-01 09:44:22

+0

难道你不能只给每个线程一个文件?这样线程就不必知道彼此了。如果服务器是Linuc/unix,我的第一种方法是为每个文件产生一个gnu grep命令,因为gnu grep是搜索文件最快的方法之一。 – 2011-04-01 12:35:54