はじめに
Javaで非同期I/Oプログラミングを行なうには、バージョン1.4から追加されたNIO(New I/O)を使用する。
NIOをラップした非同期フレームワーク「JBoss Netty」についてご紹介。
Javaでの同期I/O
Javaで同期I/Oを行なう場合、
java.ioパッケージの
- InputStreamクラス
- OutputStreamクラス
- InputStreamReaderクラス
- BufferedReaderクラス
または、java.netパッケージの
- ServerSocketクラス
- Socketクラス
を使う。
Javaでの非同期I/O
Javaで非同期I/Oを行なうには、Java1.4から実装されたjava.nioパッケージの
NIO API
を使う。
同期I/OとClock問題
同期I/Oを使ったサーバプログラム
public class SynchronousServer {
public static void main(String[] args) throws Exception {
// スレッドプールを作成する
ExecutorService threadPool = Executors.newCachedThreadPool();
// サーバソケットを作成し、8080番ポートにバインドする
ServerSocket server = new ServerSocket(8080);
while (true) {
// クライアントの接続を受け付ける
final Socket client = server.accept();
// スレッドプールにクライアントの入出力処理を渡す
threadPool.submit(new Runnable() {
public void run() {
// メソッド本体
doSomeInputAndOutput(client);
}
});
}
}
}
シングルスレッドのままだと1度に1つのクライアントしか処理できないため、スレッドプールを使い、入出力処理をワーカスレッドに任せる。
スレッド数が1万を超えるとメモリ消費やコンテキストスイッチにパフォーマンス低下が著しく、サーバが不安定に。
同期I/Oのマルチスレッド方式がネックとなり、1台のサーバで大量の同時接続数を受け付けることが難しくなる。・・・C10K問題
非同期I/Oとイベント駆動モデル
NIOによる非同期型サーバプログラムの例
public class AsynchronousServer {
public static void main(String[] args) throws Exeption {
// サーバチャンネルを作成する
ServerSocketChannel server = ServerSocketChannel.open();
// ソケットを8080番ポートにバインドする
server.socket().bind(new InetSocketAddress(8080));
// チャネルをノンブロキングモードにする
server.configureBlocking(false);
// セレクタを作成する
Selector selector = Selector.open();
// セレクタを接続受け付け用に登録する
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// I/O処理可能なチャネルを選択する
selector.select();
// I/O処理可能なチャネルのキー一覧を取得する
Iterator keys = selectedKeys.iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
// 接続可能なキーの場合
if (key.isAcceptable()) {
// クライアントを受け付ける
SocketChannel client = server.accept();
if (client != null) {
// チャネルをノンブロッキングモードにする
client.configureClocking(false);
// セレクタをクライアントからの読み込み用に登録する
client.register(selector, SelectionKey.OP_READ);
}
}
// 読み込み可能なキーの場合
if (key.isReadable()) {
// クライアントのチャネルを取得する
SocketChannel client = (SocketChannel) key.channel();
// クライアントの入出力処理を行なう
doSomeAsyncInputAndOutput(client);
}
// キーを処理済みとして削除する
keys.remove();
}
}
}
}
非同期I/Oのソケット通信速度は同期I/Oに劣る。