2011-04-01 90 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