2009-10-10 37 views
7

什麼是Java多線程應用程序的有效方式,其中許多線程必須讀取完全相同的文件(大小大於1GB)並將其作爲輸入流公開?我注意到,如果有很多線程(> 32),系統開始爭奪I/O並且有很多I/O等待。閱讀單個大文件的Java多線程

我曾考慮將文件加載到所有線程共享的字節數組中 - 每個線程都會創建一個ByteArrayInputStream,但分配一個1GB的字節數組效果不佳。

我也考慮過使用單個FileChannel,每個線程使用Channels.newInputStream()在它上面創建一個InputStream,但是它似乎是維護InputStream狀態的FileChannel。

+1

每個線程是否需要文件的全部內容?或者每個人都可以尋求它需要的相關數據? – 2009-10-10 06:37:05

+0

每個線程需要讀取整個文件。 – bob 2009-10-10 22:06:04

+0

該系統有8GB的內存,我不介意分配1GB的陣列。但是JVM似乎並不喜歡這樣 - 它使用100%cpu試圖分配數組很長時間。 – bob 2009-10-10 22:08:18

回答

10

在我看來,如果你想避免IO爭用,你要加載文件到內存中。操作系統會做一些緩衝,但如果你發現這還不夠,你將不得不自己做。

你真的需要32個線程嗎?大概你沒有那麼多的內核 - 所以使用更少的線程,你會得到更少的上下文切換等。

你的線程是否全部處理文件從開始到結束?如果是這樣,你可以有效地將文件分成塊嗎?讀取數據的第一個(比如說)10MB內存,讓所有的線程處理它,然後繼續前進到下一個10MB等

如果不爲你工作,多少內存你有與比較文件的大小?如果你有足夠的內存,但你不想分配一個巨大的數組,你可以將整個文件讀入內存,但是分成許多單獨的較小的字節數組。然後您必須編寫一個跨越所有這些字節數組的輸入流,但這應該是可行的。

+0

@jon,是否有可能使用nio工具將Java結構映射到磁盤上的文件,因此只需寫出java結構並讓JVM/OS找出如何處理實際的讀取細節? – 2009-10-10 08:56:44

+1

@Thorbjorn:Java支持內存映射文件,但是如果你有比操作系統更多關於如何使用這個文件的信息,你可能會做得更好。 – 2009-10-10 08:59:37

1

幾個想法:

  1. 寫充當視圖到FileChannel定製的InputStream實現。這樣寫,它不依賴於FileChannel中的任何狀態。 (即:每個實例應該跟蹤自己的位置,閱讀應該使用基礎FileChannel的絕對讀取。)這至少可以解決您使用Channels.newInputStream()所遇到的問題,但它可能無法解決您的IO爭用問題。

  2. 編寫一個自定義的InputStream實現,該實現充當MappedByteBuffer上的視圖。內存映射不應該像實際一次將所有內容讀入內存一樣糟糕,但是您仍然會佔用1GB的虛擬地址空間。

  3. 與#1相同,但具有某種共享緩存層。除非1結果不夠有效,2不可行,否則我不會嘗試。真的,操作系統應該已經在#1爲你做了一些緩存,所以你在本質上要比操作系統文件系統緩存更聰明。

5

您可以在只讀模式下多次打開文件。您可以以任何您想要的方式訪問該文件。只需將緩存留給操作系統即可。如果速度太慢,可能會考慮某種基於塊的緩存,其中所有線程都可以訪問相同的緩存。

0

這是一個非常大的文件。你能否將文件作爲一組較小的文件傳送?即使在公司網絡上傳送這個文件也是一項很大的工作。

有時比程序更容易改變過程。

你甚至可以寫一些東西把文件分割成多個塊並分別處理它們。