私いくつかの時間のブログの前にJavaのネットワークプログラミング-トランシーバマルチスレッド並列データサーバとクライアント間のトランシーバパラレル練習をまとめました。原理はその後、遺体が実行を読み書きするために待機してブロックされたスレッド、2つのスレッドが読み取りおよび書き込み操作を担当している、単一のクライアントのために、あること、サービスリーチ非常に簡単です。
実際には、このモデルは非常に悪いです。サーバー上の各クライアントが1000台のクライアントが存在する場合、あなたは2000+スレッドに必要な、二つのスレッドを占有する必要があるため。CPUはコンテキストスイッチをスレッドに多くの時間を要し、システムリソースが浪費されています。
私たちはまず、スレッドの数を減らし、ブロッキングの問題を解決したいです。NIOは、IOによって消去への読み込みと書き込みをブロックする多重化することができます。スレッドプールと相まって、あなたがスレッドからの支援量の少ない百万クライアント接続を実現することができます。
NIOとは何ですか
NIOとIO多重化
JavaのNIOの完全名java非ブロックIO。これは文字通り、非ブロッキングIOです。実際にはここでしかマクロ引数を非ブロック。
他の人がブログを引用したIOモード、について、いくつかのIOモードの違いについて説明します。
同期IO、非同期IO、IO間のブロッキング接続と違い、非ブロックIOブリーフ
このブログはちょうど私がIO多重化モデルのいずれかに属しNIOを言いたい、これらを繰り返すことはありません。(Labはこの部分を見てみなければならない流行を終わらせるために学校に、「UNIXネットワークプログラミング」のバックを持っています)
多重化されたIOモデル、唯一のIO操作を呼び出すために、ソケットは読み取りと書き込みのイベント時に複数の状態は、ソケットのポーリングを継続するスレッドがあります。それは、複数のソケットを管理するためのスレッドであるため、システムは準備ができたら、それはかなりの資源の消費を減らすので、IOリソースを使用する、唯一のソケット、保守スレッドを別のスレッドを作成する必要はありません。
任意使用selector.selectがあるか否かのJava NIO()イベントに到達するために複数のチャネルを監視し、何のイベントは、イベントがIO処理と呼ばれ、ブロックされていません。
3つのコア
- チャンネル(チャンネル)
- バッファ(バッファ)
- セレクタ(セレクタ)
詳細は以下の通り
応用例NIO
ここで例えば、サーバープロセスにクライアントのメッセージを読み取り、(かかわらず、当分の間、出力の、入力のみフルコンテンツ)NIOの使用を記載しています。下に示すようなフローチャートを描きます。
- そして、のServerSocketChannelセレクタを確立し、以下のように、登録、接続要求のためのクライアントのリッスンを結合:
selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
// 设置为非阻塞
server.configureBlocking(false);
// 绑定本地端口
server.socket().bind(new InetSocketAddress(port));
// 注册客户端连接到达监听
server.register(selector, SelectionKey.OP_ACCEPT);
しかし、またreadSelectorとwriteSelectorを確立します。実際には、スレッドプールは、当面、ここで書いていない、事前に確立されています。
readSelector = Selector.open();
writeSelector = Selector.open();
- 、チャネルを監視クライアントを与え、その後のクライアントメッセージを監視するため、SocketChannelへの確立
//select()方法返回已就绪的通道数
if (selector.select() == 0) {
continue;
}
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
// 检查当前Key的状态是否是accept的
// 客户端到达状态
if (key.isAcceptable()) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
// 非阻塞状态拿到客户端连接
SocketChannel socketChannel = serverSocketChannel.accept();
try {
// 客户端构建异步线程
// 添加同步处理
//此处代码暂且忽略
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端连接异常:" + e.getMessage());
}
}
- SocketChannelはreadSelectorとwriteSelectorに登録しました
/**
*参数分别是:channel,对应的selector,以及
*registerOps:待注册的操作集,这个在后文中有详细解析;
*locker:用于标识同步代码块的状态,是锁定还是可用;
*runnable:执行具体读写操作的类,送给线程池执行;
*map:建立SelectionKey与Runnable映射关系的HashMap。
*/
private static SelectionKey registerSelection(SocketChannel channel, Selector selector,
int registerOps, AtomicBoolean locker,
HashMap<SelectionKey, Runnable> map,
Runnable runnable) {
synchronized (locker) {
// 设置锁定状态
locker.set(true);
try {
// 唤醒当前的selector,让selector不处于select()状态
//注册channel时一定要将selector唤醒,否则当前select中没有刚注册的channel
selector.wakeup();
SelectionKey key = null;
if (channel.isRegistered()) {
// 查询是否已经注册过
key = channel.keyFor(selector);
if (key != null) {
//将新的Ops添加进去
key.interestOps(key.readyOps() | registerOps);
}
}
if (key == null) {
// 注册selector得到Key
key = channel.register(selector, registerOps);
// 注册回调
map.put(key, runnable);
}
return key;
} catch (ClosedChannelException e) {
return null;
} finally {
// 解除锁定状态
locker.set(false);
try {
// 通知
locker.notify();
} catch (Exception ignored) {
}
}
}
}
- selectionKeysを通してチャネルを得るために、各クライアントのメッセージを監視し、その後、入力操作を行います
try {
if (readSelector.select() == 0) {
continue;
}
Set<SelectionKey> selectionKeys = readSelector.selectedKeys();
for (SelectionKey selectionKey : selectionKeys) {
if (selectionKey.isValid()) {
//IO处理代码,暂且忽略
}
}
selectionKeys.clear();
} catch (IOException e) {
e.printStackTrace();
}
注:これらは、そこには完全に一緒にされ、スレッドプールの呼び出しクラスオブジェクト、メソッド呼び出しやキー操作などの一部を省略して、いくつかのコードの断片です。しかし、基本的なアプローチは、ブログの残りの部分は、ピットを埋めるために戻って、表示されています。
コードを見てみると、上記の、いくつかの認知NIO方法は非常にあいまいです。方法の一部の理解を深め、ソースセレクタとSelectionKeyクラスとクラスを読め以下のコメント。
クラスセレクタ
NIOセレクタは、ここでは、コアクラスであるいくつかの重要なメソッドセレクタです:
- オープン関連
- オープン()セレクタをオープン
- パブリック抽象ブールいるisOpen();オープンにするかどうかを判断します
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
- キー関連
- 公共の抽象セット
キーは();キーセットのすべてを返します - 公共の抽象セット
selectedKeys();リターンキーセットが選択されています
- 公共の抽象セット
- 選択する
- いくつかの方法は、チャネルの数は、0であってもよい次の準備リターンです。
- selectNow()、非ブロッキング方法。
- SELECT()、一つのチャンネルが選択される;.方法2ウェイクアップコールを、3つの場合のみ返される; 3.スレッド割り込み。
- )(ブロックされていない状態、即ち、タイムアウト複数の選択よりも、(タイムアウト)を選択します。
- ウェイクアップ()は、すぐに戻り、ブロックし、ブロッキングリフトにメソッドを選択します
- クローズ()、セレクタを閉じ。
SelectionKeyクラス
チャネルセレクタに登録されたが、とSelectionKeyオブジェクトを参照するために使用されます。
操作集
- 操作セット:オペレーションのセット、各種の操作を表すいくつかの定数int値、;選択キーは、2つの操作のセット、関心のセット、及びすぐ操作セットを含みます
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;
-
対象セット:セットの関心、すべてのチャンネル操作のセット; interestOps(int型)によって更新することができる方法
-
既製操作セット:セットレディ操作、チャンネルと、報告される準備ができてまたは基礎操作によって更新動作のみを含むように、一方のチャネルが読み取る準備ができている場合、例えば、読み出し動作が準備現在の濃度まで添加します。
メソッド一覧
- パブリック抽象のSelectableChannelチャネルは():このキーが取り消された場合でも、まだ返され、この選択キーに関連付けられたチャンネルを返し;.
- パブリック抽象セレクターセレクターは、():このキーは、まだリターンをキャンセルされた場合でも、関連するキーを選択するには、このセレクタを返します。
- パブリック抽象ブールのisValid():検出キーが解除され、またはチャネルがクローズされ、またはセレクタが、これは無効なキーのAbstractSelector.removeKey(キー)になります閉じているときにこのキーが原因とSelectionKeyに設定され、有効です。無効;
- パブリック抽象ボイド)(キャンセル:成功して戻った場合、登録要求をキャンセルするには、このキーを、キーが無効である、有効なプロパティは.cancel cancelledKeysにカウンタに加算されてセレクタのキー操作が虚偽である、と(selector.cancelを実行します。キー)(今後の鍵がcancelledkeyコレクションに参加)。
- パブリック抽象int型interesOps():このキーセットはinteresを得ました。
- パブリック抽象とSelectionKeyのinterestOps(int型OPS):指定された値にinterstこのキーと、これは例外がスローされます、現在のチャンネルをサポートしていない場合オプスオプスchannel.validOpsを確認し、この意志;.
- パブリック抽象int型readyOps():現在のチャンネルのイベントの所定の場所にすでにあるこの操作のキーセットの準備をする;.
- 。!そこisWritable()、isConnectable()、isAcceptable();(readyOps()&OP_READ)= 0:公共の最後のブールisReadable():このキーはイベントの準備と等価である "読み" が検出されました
- 公共の最終的なオブジェクトは、アタッチ(オブジェクトOB):このキー上の添付ファイルとして指定されたオブジェクトは、アタッチメントの複数の有効期間OPSイベントキーに転送することができる;.
- 公共の最終的なオブジェクトの添付ファイルは、():チャネルのアクセサリー添付ファイルを取得し、あなたはその後、現在のチャネル(またはとSelectionKeyでは)ライフサイクルを共有することができますが、データは、ネットワーク内の添付ファイルのソケットデータ伝送としてではありません。
最後に終了し、このブログでは唯一のNIO、言及していない何かの簡単な紹介とみなすことができます。チャネル方式とバッファは、分析の一部ではない、スレッドプールは一部に加え、同様に出力されていないセットを操作し、私が話すことはありませんでした。いつものように、はるかに詳細完了したいが、それは次のようになったので、より深く、より広大な知識は、我々は、コンテンツの一部を放棄することができます。複数のブログの書き込みを開くための詳細な計画が良くなる場合。