JAVA NIOソース分析---まとめ編

4471 ワード

前回のJAVA NIOのソースコードの分析を通して、いくつかの重要なコードの実現について探究した.
一、ソース分析プロセスの整理.
1.Selector.Open()セレクタを取得すると、異なるオペレーティングシステムに基づいてSelector実装クラスが作成され、実装クラスはチャネルハンドルとイベントタイプを保存するためのデータ構造PollArrayWrapperが作成され、Windowsシステムであれば相互に接続されたsocketチャネルシミュレーションパイプのペアが起動のために作成され、一方、Linuxは、カーネルバージョン>=2.6、JDK>1.5 u 9の場合、より効率的なepollモードのSelector実装クラスを作成することができます.またLinuxシステムはオペレーティングシステムのパイプを直接利用して起動する機能を実現することができる.
 
2.ServerSocketChannel.register(...)チャネル登録は、channelとselectorに基づいて、両方を関連付けたSelectionKeyImplオブジェクトを作成し、登録キーのセットに記録し、PollArrayWrapper構造にsocketハンドルおよびイベントを追加します.また、オペレーティングシステムの最大ハンドル数の制限には、より多くのスレッドを作成する必要がある場合があります.
 
3.Selector.select();選択操作を実行する前に、まずキャンセルしたキーセットをクリーンアップし、スレッド数(最大ハンドル数制限で作成されたhelperスレッド)を調整し、オペレーティングシステムの最下位レベルのselect関数を呼び出し、チャネルが準備されているかどうかをチェックする作業をオペレーティングシステムに移行し、Windowsであればポーリングで検出します.Linuxの場合、select/pollもjdkのバージョンとカーネルのバージョンに応じてポーリングまたは中断に基づくより効率的なepollに基づいて検出される可能性があります.システムの最下位レベルのselect呼び出しが返されると、キャンセルされたキーセットのクリーンアップが再び行われ、前回のselect操作に続いて今回終了するまでに新たに準備されたチャネル数が返されます.
 
4.Selector.wakeup()は、パイプライン(Windowsは相互接続されたsocketチャネル)のsink側に1バイトを書き込むことでselectにブロックされた呼び出しを呼び出し、連続した複数回の呼び出しは1回の呼び出しに等しい.
 
二、関連知識点
1.まずSelector.Open()は単一のモードではありません.静的メソッドを呼び出すたびに、新しいSelectorインスタンスが返されます.
 
2.SelectorはconfigureBlockingを呼び出すことで、非ブロックモードを有効にするかどうかを設定できます.デフォルトはブロッキングモードです.
 
3.サービス側とクライアントが同じSelectorを維持しているかどうか、答えは否定的であり、サービス側とクライアントはそれぞれ1つのSelectorオブジェクトを維持しており、マルチスレッドが同時発生している場合、Selectorはスレッドが安全であるが、その内部の重要メンバーの集合(registeredKeys、selectedKeys、cancelledKeys)は非スレッドが安全であることに注意している.複数のスレッドがセレクタのキーのセットに同時にアクセスするときに問題がある場合は、アクセスを合理的に同期するには、いくつかのステップを実行します.
 
4.選択操作が実行されると、セレクタはSelectorオブジェクト上で同期し、登録されたキーのセット、最後に選択されたキーのセットの順になります.マルチスレッドのシーンでは、直接変更や他の操作による副作用にかかわらず、キーのセットを変更する必要がある場合は、まず同じ順序で同じオブジェクト上で同期する必要があります.ロックのプロセスは非常に重要です.競合するスレッドが同じ順序でロックを要求していない場合、デッドロックの可能性があります.他のスレッドがセレクタに同時にアクセスしないことを確認できる場合は、同期する必要はありません.
 
5.channel.read()関数は-1を返しますが、いつ-1を読みますか?サーバ側では、クライアントがchannelを呼び出す.close()が接続を閉じると,サーバ側から返される読み出し数は−1であり,終了したことを示す.では、この場合、対応するSelectionKeyをcancelに削除する必要があります.これは、selectorがこのchannel上の読み取りイベントを傍受せず、channelを閉じることを意味します.
 
6.1つのチャネルを複数のセレクタに登録することができるが、各セレクタに対して1回しか登録できない.
 
7.チャネルが閉じると、関連するすべてのキーが自動的にキャンセルされます.セレクタが閉じると、セレクタに登録されているすべてのチャネルがログアウトされ、関連するキーがすぐに無効化されます.
 
8.注意select()の動作戻り値は、準備されたチャネルの総数ではなく、前のselect()から呼び出された後に準備完了状態に入ったチャネルの数である.以前の呼び出しで準備ができており、今回の呼び出しで準備ができているチャネルは計上されず、前回の呼び出しで準備ができているが準備ができていないチャネルも計上されません.これらのチャネルは、選択したキーのセットに残っている可能性がありますが、戻り値にはカウントされません.戻り値は0である可能性があります.これも、0を返すときにcontinueが必要な理由です.
 
9.Selectorクラスのclose()メソッドはselect()メソッドの同期方式と同じであるため,ブロックし続ける可能性もある.選択プロセスがまだ進行中である場合、close()に対する呼び出しは、選択プロセスが終了するまでブロックされ、または選択を実行するスレッドがスリープに入る.
 
10.チャネルが閉じると、関連するキーもキャンセルされます.これは、進行中のselect()には影響しませんが、select()を呼び出す前に有効なキーであり、戻ると無効になる可能性があります.
三、SelectionKey
selectionKeyは、チャネル(channel)とセレクタ(selector)との間の登録関係、およびチャネルを維持したイベントを表す.
SelectionKeyには、2つのセット(実際には整数で符号化されたbyteマスク)が含まれています.
1、登録されている興味のある操作集合、すなわちinterestOps集合.
2、準備した操作集合(準備集合)即ちreadyOps集合.
 
Selectorは3つのコレクションを維持しました.
1、登録されたキーセット呼び出し、keys()2、選択されたキーセット呼び出し、selectedKeys()3、キャンセルされたキーセットプライベート、cancelledKeys
 
これらのコレクション間のフロー関係:
1.cancel操作を呼び出す場合、キャンセルするキーをcancelledKeysキーセットに追加するだけで、次回selectが呼び出されるまで待つ必要があります.しかし、SelectionKeyのisValid()はすぐにfalseに返信します.
2.オペレーティングシステムが準備完了のチャネルに戻るとき:
a)チャネルのselectionkeyが選択されたキーのセット(selectedKeys)にまだ存在しない場合、キーのreadyOpsセットは空にされ、オペレーティングシステムが発見した現在のチャネルが準備した動作を示すビットマスクが設定される.
b)そうでなければ、チャネルのキーが選択されたキーのセットに入れられると、readyセットは消去されず、蓄積される.すなわち,以前の状態がreadyの操作であれば,今回はreadyではないが,bitビットは依然としてreadyであり,クリアされない. 
2から、selectedKeysをループするたびにitを呼び出す必要がある理由がわかります.remove().
 
また、1つのchannelのデータが読み終わっていない(または処理しないデータがある)場合、このchannelは常に準備状態にあるため、selectorのselectedKeys()メソッドのたびに、このchannelに関連付けられたselectionkeyを返すことができ、channelのデータを読み終わったり、このselectionKeyをcancelに落としたりしない限り、select()を繰り返し続けます.
 
二、select()を中断する方法
select()メソッドはブロックされ、channelが準備されてから返されます.ブロックを停止し、selectメソッドを中断してスレッドを継続させる場合があります.
3つの方法があります.1,wakeup()これは優雅な方法であり、selectにブロックされたスレッドをすぐに返す.現在selectにブロックされていない場合、今回のwakeup呼び出しは、次のselect操作に作用します.つまり、次のselect呼び出しは、準備ができているかどうかにかかわらず、すぐに戻ります.2,close()セレクタのcloseが呼び出されると,選択操作でブロックされたすべてのスレッドが呼び出され,関連チャネルがログアウトされ,キーもキャンセルされる.3,interrupt()は実際にinterruptがスレッドを中断しない.スレッド割り込みフラグを設定します.その後もwakeup()が呼び出されます.これは、SelectorがinterruptedExceptionをキャプチャし、例外処理でwakeup()を呼び出したためです.