2010-01-08 57 views
3

我正在使用java.nio.channels.Selector,我想爲每個已準備好讀/寫/接受的selectedKey創建一個單獨的線程,但是我想要以確保相同的套接字永遠不會被兩個不同的線程同時處理。最好的辦法是什麼?我想在創建線程之前取消每個selectedKey,它將處理它的套接字,並在線程完成它的生命時重新註冊套接字到選擇器,但我不知道這將是多麼高效......java.nio.channels.Selector的併發處理

回答

9

有一個很好的Doug Lea的presentation約可擴展的I/O在Java中,建設我的服務器時,我緊隨其後。我採取以下方法:

我有一個單一的I/O線程在我的「反應堆」,只執行I/O(和非常基本的解碼/編碼);它只是在字節和消息對象之間進行轉換,然後將傳入的消息對象傳遞給線程池以進行業務邏輯處理。我強烈推薦這種方法 - 除非你的I/O線程變得飽和,否則不需要多於一個I/O線程,我想大多數I/O瓶頸是因爲其它處理髮生在這個線程上。

如果您可以證明您的I/O線程已飽和,您可以按照演示中建議的「多個反應堆」模式進行操作,即主反應堆接受傳入連接,然後將它們交給執行處理的子反應堆。每個兒童反應器在總連接的一個子集之間進行多路複用,因此不存在多於一個線程與給定SelectionKey交互的危險。

+0

我的第一反應是推薦這個。不過,我不確定這些讀/寫/接受操作中的哪些操作不會因爲「準備就緒」而導致延遲。但是,如果Doug Lea也推薦這樣做,這可能是一個很好的做事方式。 – 2010-01-08 12:58:02

1

我認爲爲每個套接字創建一個單獨的線程可能會導致太多。另外,創建一個新的Thread在執行時間是很昂貴的。您應該限制活動線程的數量並通過使用線程池來限制新線程的創建。 java.util.concurrent.Executors提供了創建固定線程池的功能。詳情請見http://java.sun.com/docs/books/tutorial/essential/concurrency/pools.html

如果它的套接字要防止被多個線程一次擊中,我會考慮最簡單的排除:鎖定套接字對象。可能會有更有效的策略,但可能並非簡單或更簡單。

更新

如果在一些早期返回插座仍在處理其他選擇完成後,你可以使用線程互相干擾而告終。通過鎖定關閉其他線程是可能的,但不是真正的優雅解決方案(對不起)。

兩種選擇,我能想到的:

  • 註銷該通道就可以開始你的處理線程之前,以及在處理活動結束時重新註冊。聽起來很舒服,但應該完成工作。

  • 維護您自己的正在進行中的渠道的數據結構,例如,一個Set,並在將其發送給線程之前向該集合添加新發現的通道,並在從線程返回之前將其刪除。在處理來自選擇集的頻道時,忽略已經在該集中的任何頻道。所有這一套使用都需要同步。

+0

嗨,謝謝你的回覆。 我正在使用線程池。 我的問題是,套接字仍然被註冊到Selector,同時仍然由池中的線程處理。 這可能會導致線程池的新任務不必要的創建,該任務將等待套接字的發佈,以便發現必需的工作已經完成。 這是我試圖阻止的第二項任務的創建。 – yuri 2010-01-08 12:34:24

+0

對不起,這很慢。我已經用更多建議更新了我的答案。 – 2010-01-08 12:56:14