Java NIO の核心コンポーネントとバッファ操作の詳細

NIO の概要

従来の Java I/O(BIO)は、I/O 操作中にスレッドがブロッキングされる同期モデルです。これにより、多数の同時接続を処理する際にスレッド数が増加し、パフォーマンスのボトルネックが発生します。これを解決するために Java 1.4 で導入されたのが NIO(New I/O)です。NIO は非ブロッキング I/O、バッファ指向、チャネルベースの設計を採用し、少数のスレッドで多数の接続を効率的に処理できます。

ただし、NIO の性能上の利点は高並列・高遅延なネットワーク環境で顕著であり、接続数が少ない場合や低遅延環境では必ずしも BIO より優れているわけではありません。

NIO の主要コンポーネント

NIO は以下の3つの主要コンポーネントで構成されます:

  • Buffer(バッファ):データの読み書きを一時的に保持する領域。すべての I/O 操作はバッファ経由で行われます。
  • Channel(チャネル):双方向のデータ転送経路。ファイルやソケットなどとの接続を表し、バッファとの間でデータを送受信します。
  • Selector(セレクタ):複数のチャネルを単一スレッドで監視するための多路複用機構。イベント駆動型の非ブロッキング I/O を実現します。

BIO がストリームベースであるのに対し、NIO は「チャネルを通じてバッファを運ぶ」モデルです。チャネルはデータそのものに触れず、あくまで輸送路として機能します。また、チャネルは双方向であるため、同一チャネルで読み書きが可能です。

バッファ(Buffer)の詳細

Buffer は抽象クラスであり、実際には ByteBufferCharBuffer などの具象クラスが使用されます。特に ByteBuffer はバイトデータの操作に広く使われます。

バッファには以下の4つの重要な内部状態変数があります:

// 状態の関係: 0 <= mark <= position <= limit <= capacity

private int mark = -1;      // 復帰位置(任意)
private int position = 0;   // 次に読み書きするインデックス
private int limit;          // 読み書き可能な上限位置
private int capacity;       // バッファの最大容量(不変)

バッファは「書き込みモード」と「読み込みモード」の2つの状態を持ちます。作成直後は書き込みモードで、flip() を呼び出すことで読み込みモードに切り替わります。再び書き込みモードに戻すには clear() または compact() を使用します。

バッファのインスタンスはコンストラクタではなく、静的ファクトリメソッドで生成します。例:

// ヒープ上に割り当て
ByteBuffer heapBuf = ByteBuffer.allocate(1024);

// ダイレクトメモリ(ネイティブメモリ)に割り当て
ByteBuffer directBuf = ByteBuffer.allocateDirect(1024);

主な操作メソッド:

  • put():データを書き込む
  • get():データを読み取る
  • flip():書き込み→読み込みモードに切り替え(limit = position; position = 0;
  • clear():全内容を無効化し、書き込みモードにリセット(position = 0; limit = capacity;

以下は CharBuffer の動作を示す例です:

import java.nio.CharBuffer;

public class BufferExample {
    public static void main(String[] args) {
        CharBuffer buf = CharBuffer.allocate(8);
        printStatus(buf, "初期状態");

        buf.put('X').put('Y').put('Z');
        printStatus(buf, "3文字書き込み後");

        buf.flip();
        printStatus(buf, "flip() 呼び出し後");

        while (buf.hasRemaining()) {
            System.out.print(buf.get());
        }
        System.out.println();
    }

    static void printStatus(CharBuffer b, String label) {
        System.out.printf("%s → pos=%d, lim=%d, cap=%d%n", 
                          label, b.position(), b.limit(), b.capacity());
    }
}

タグ: Java NIO ByteBuffer Channel Selector

5月19日 21:15 投稿