簡體   English   中英

基於可用的免費cpu的Java並發

[英]Java concurrency based on available FREE cpu

當且僅當有空閑cpu時,如何擴展以使用更多線程? 類似於ThreadPoolExecutor的東西,當cpu核心空閑時使用更多線程,如果沒有則更少或僅使用一個線程。

使用案例

當前情況:我的Java服務器應用程序處理請求並提供結果。 有一個ThreadPoolExecutor按照以下原則為具有合理數量的最大線程的請求提供服務:cpu核心數=最大線程數。 執行的工作很重,並且有一些磁盤IO(DB)。 代碼是線性的,單線程的。 單個請求需要50到500毫秒才能處理。 有時每分鍾只有幾個請求,有時同時有30個請求。 具有12個內核的現代服務器可以很好地處理負載。 吞吐量很好,延遲還可以。

期望的改進:當請求數量較少時,大多數情況下都是如此,許多cpu核心處於空閑狀態。 在這種情況下,可以通過為單個請求多線程運行一些代碼來改進延遲。 一些原型設計顯示了改進,但是一旦我測試了更多的並發請求,服務器就會變成香蕉。 吞吐量下降,內存消耗過高。 30個同時請求共享隊列10意味着10個最多可以運行而20個正在等待,並且10個中的每一個一次最多使用8個線程用於並行,對於具有12個核心的機器來說似乎太多了(其中6是虛擬的)。

在我看來,這似乎是一個常見的用例,但我無法通過搜索找到信息。

IDEAS

1)請求計數一個想法是計算當前處理的請求數。 如果為1或低則執行更多並行操作,如果為高則不執行任何操作並繼續執行單線程,如前所述。 這聽起來很簡單。 缺點是:請求計數器重置不能包含錯誤,最后想一想。 它實際上並沒有檢查可用的cpu,也許另一個進程也使用cpu。 在我的情況下,機器專用於這個應用程序,但仍然。

2)實際的cpu查詢我認為正確的方法是只詢問cpu,然后再決定。 由於Java7有OperatingSystemMXBean.getSystemCpuLoad(),請參閱http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/OperatingSystemMXBean.html#getSystemCpuLoad()但我可以找不到任何提到getSystemCpuLoad和ThreadPoolExecutor的網頁,或類似的關鍵字組合,這告訴我這不是一條好路。 JavaDoc說“返回”整個系統最近的cpu使用情況“,我想知道”最近的cpu使用情況“是什么意思,最近的情況以及調用的成本。

UPDATE

我暫時擱置了這個問題,看看是否會有更多的輸入。 不。 雖然我不喜歡技術問題的“無能為力”的答案,但我現在接受霍爾格的回答。 他有良好的聲譽,良好的論據,其他人已經批准了他的答案。 我自己曾經嘗試過2個想法。 我查詢任務中的getSystemCpuLoad()來決定他們自己的ExecutorService有多大。 正如Holger所寫,當存在SINGLE ExecutorService時,可以很好地管理資源。 但是一旦任務開始他們自己的任務,他們就不能 - 這對我來說沒有用。

沒有辦法根據“免費CPU”進行限制,無論如何它都無法工作。 有關“免費CPU”的信息在您獲得后立即過時。 假設您有12個並發運行的線程並同時檢測到有一個空閑的CPU核心並決定安排子任務......

您可以做的是限制最大資源消耗,當使用具有所有任務的最大線程數的單個ExecutorService ,該消耗非常有效。

棘手的部分是任務對子任務的結果的依賴性,這些子任務在以后排隊,並且由於工作線程的數量有限,可能仍然處於未決狀態。

如果任務檢測到其子任務仍處於掛起狀態,則可以通過撤消並行執行來調整此值。 為此,請手動為子任務創建FutureTask ,並使用execute而不是submit安排它。 然后在正常情況下繼續執行任務,並在順序實現中執行子任務的位置檢查是否可以從ThreadPoolExecutor remove FutureTask cancel不同,這只有在尚未啟動的情況下才有效,因此它是一個沒有空閑線程的指示器。 因此,如果remove返回true ,則可以就地執行子任務,讓所有其他線程執行任務而不是子任務。 否則,您可以等待結果。

在這個地方值得注意的是,如果任務適應I / O操作(或者可能等待子任務),擁有比CPU核心更多的線程是可以的。 這里重要的是要有一個限制。

FutureTask<Integer> coWorker = new FutureTask<>(/* callable wrapping sub-task*/);
executor.execute(coWorker);

// proceed in the task’s sequence

if(executor.remove(coWorker)) coWorker.run();// do in-place if needed
subTaskResult=coWorker.get();

// proceed

聽起來像Java 7中引入的ForkJoinPool正是您所需要的。 ForkJoinPool專門設計用於使所有CPU完全忙碌,這意味着存在與CPU一樣多的線程,並且所有這些線程也在工作而不是阻塞(對於后者,請確保使用ManagedBlocker進行數據庫查詢)。

ForkJoinTask有一個方法getSurplusQueuedTaskCount ,JavaDoc getSurplusQueuedTaskCount說“這個值可能對於是否分叉其他任務的啟發式決策很有用。” 因此可以更好地替代getSystemCpuLoad解決方案,以便對任務分解做出決策。 這允許您在系統負載較高時減少分解次數,從而減少任務分解開銷的影響。

另請參閱我的答案以獲得有關Fork / Join-pools原理的更深入的解釋。

暫無
暫無

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

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