簡體   English   中英

Java和隊列:多線程I / O的飽和問題

[英]Java and queues: saturation issues with multithreaded I/O

這個問題與最新版本的Java有關。

30個生成器線程將字符串推送到抽象隊列。 一個編寫器線程從同一隊列彈出,並將該字符串寫入駐留在5400 rpm HDD RAID陣列上的文件。 數據以大約111 MBps的速率推送,並以大約80MBps的速率彈出/寫入。 該程序存在5600秒,足以使大約176 GB的數據在隊列中累積。 另一方面,我限制為總共64GB的主內存。

我的問題是:我應該使用什么類型的隊列?

這是我到目前為止所嘗試的內容。

1) ArrayBlockingQueue 這個有界隊列的問題在於,無論數組的初始大小如何,一旦填滿,我總是會遇到活動問題。 實際上,在程序啟動幾秒后, top只報告一個活動線程。 分析表明,平均而言,生產者線程花費大部分時間等待隊列釋放。 這與我是否使用公平訪問策略(構造函數中的第二個參數設置為true)無關。

2) ConcurrentLinkedQueue 就活力而言,這個無限制的隊列表現得更好。 直到我耗盡內存,大約七百秒,所有三十個生產者線程都處於活動狀態。 然而,在我超過64GB限制之后,事情變得異常緩慢。 我猜想這是因為分頁問題,​​雖然我沒有進行任何實驗來證明這一點。

我預見到兩種方式可以解決我的問題。

1)購買SSD。 希望I / O速率提高會有所幫助。

2)在寫入文件之前壓縮輸出流。

還有其他選擇嗎? 我是否遺漏了構造/使用上述隊列的方式? 是否有更聰明的方式來使用它們? Java Concurrency in Practice一書提出了一些飽和策略(第8.3.3節),在有限隊列填充速度快於耗盡的情況下,但不幸的是,沒有一個 - 中止,調用者運行,以及兩個丟棄政策---適用於我的方案。

尋找瓶頸。 你產生的消耗量更多,有界的隊列絕對有意義,因為你不想耗盡內存。

盡量讓您的消費者更快。 剖析並查看花費最多時間的位置。 既然你在這里寫了一些想法:

  • 你能用NIO來解決問題嗎? (也許是FileChannel#transferTo()
  • 僅在需要時沖洗。
  • 如果你有足夠的CPU儲備,壓縮流? (正如你已經提到的那樣)
  • 優化磁盤以提高速度(raid緩存等)
  • 更快的磁盤

正如@Flavio已經說過的那樣,對於生產者 - 消費者模式,我認為沒有任何問題,它應該是現在的樣子。 最后,最慢的一方控制速度。

我在這里看不到問題。 在生產者 - 消費者的情況下,系統將始終以較慢的一方的速度行事。 如果生產者比消費者更快,那么當隊列填滿時,它將減慢到消費者的速度。

如果你的約束是你不能減慢生產者的速度,你將不得不找到一種方法來加速消費者。 描述消費者(不要開始太花哨,一些System.nanoTime()調用經常提供足夠的信息),檢查它花費大部分時間的地方,並從那里開始優化。 如果您有CPU瓶頸,可以改進算法,添加更多線程等。如果您有磁盤瓶頸,請嘗試少寫(壓縮是個好主意),獲得更快的磁盤,寫入兩個磁盤而不是一個...

根據java“ 隊列實現 ”,還有其他適合您的類:

  • 的LinkedBlockingQueue
  • 的PriorityBlockingQueue
  • DelayQueue
  • 的SynchronousQueue
  • LinkedTransferQueue
  • TransferQueue

我不知道這些類的性能或內存使用情況,但你可以嘗試自己。

我希望這對你有所幫助。

為什么你有30個生產者。 這個號碼是由問題域修復的,還是僅僅是您選擇的號碼? 如果是后者,你應該減少生產者的數量,直到他們生產的總費率只比消費量大一點,並使用阻塞隊列(正如其他人所建議的那樣)。 然后,您將使您的消費者忙碌,這是性能限制部分,同時最小化其他資源(內存,線程)的使用。

你只有兩種方法:讓供應商變慢或消費者更快。 較慢的生產者可以通過多種方式完成,特別是使用有界隊列。 要讓消費者更快,請嘗試https://www.google.ru/search?q=java+memory-mapped+file 請查看https://github.com/peter-lawrey/Java-Chronicle

另一種方法是從編寫字符串寫緩沖區的工作中釋放寫線程。 讓生產者線程發出就緒緩沖區,而不是字符串。 使用有限數量的緩沖區,比如2 * threadnumber = 60。 在開始時分配所有緩沖區,然后重復使用它們。 將隊列用於空緩沖區。 生成線程從該隊列中獲取緩沖區,填充它並放入寫入隊列。 編寫線程從寫入線程,寫入磁盤並放入空緩沖區隊列中獲取緩沖區。

另一種方法是使用異步I / O. 生產者自己開始編寫操作,沒有特殊的寫作線程。 完成處理程序將使用過的緩沖區返回到空緩沖區隊列中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM