Java NIO詳細メモ

33420 ワード

Java NIOノート
java.Nioフルネームjava non-blocking IOは、jdk 1を指す.4以上のバージョンで提供されている新しいapi(New IO)は、すべての元のタイプ(booleanタイプを除く)にキャッシュサポートされているデータコンテナを提供し、非ブロック的な高伸縮性ネットワークを提供することができます.
ストリームとブロック
元のI/OとNIOの最も重要な違いはデータのパッケージングと伝送の方式で、元のI/Oはストリームの方式でデータを処理して、NIOはブロックの方式でデータを処理します.
ストリーム向けI/Oは一度に1バイトのデータを処理する:1つの入力ストリームは1バイトのデータを生成し、1つの出力ストリームは1バイトのデータを消費する.ストリーミングデータのフィルタを作成するのは簡単です.いくつかのフィルタをリンクして、各フィルタが単一の複雑な処理メカニズムの一部だけを担当するようにします.不利な点は、ストリーム向けのI/Oが通常かなり遅いことです.
ブロック向けI/Oは一度に1つのデータブロックを処理し、ブロックごとの処理データはストリームごとの処理データよりずっと速い.しかし、ブロック向けのI/Oには、ストリーム向けのI/Oが持つ優雅さと簡単さが欠けている.
I/OパッケージとNIOはよく統合されていますjava.io.* NIOをベースに再実現されているので、NIOのいくつかの特性を利用することができます.例えばjava.io.* パケット内のいくつかのクラスには、ストリーム向けシステムでも処理速度が速くなるように、ブロック形式でデータを読み書きする方法が含まれています.
チャネルとバッファ
チャネルチャネルチャネルは、元のI/Oパケット内のストリームのシミュレーションであり、データの読み取りと書き込みが可能である.
チャネルとストリームの違いは、ストリームが一方向にしか移動できないことであり、チャネルは双方向であり、読み取り、書き込み、または読み取り、書き込みの両方に使用できることである.
FileChannel:ファイルからデータを読み込む
DataChannel:UDPによるネットワークデータの読み書き
SocketChannel:TCPによるネットワークデータの読み書き
ServerSocketChannel:新しく入ってきたTCP接続をリスニングでき、新しく入ってきた接続ごとにSocketChannelが作成されます(Webサーバのようです).
Bufferは、書き込みまたは読み出したばかりのデータを含むオブジェクトであり、ストリーム向けI/Oでは、データが直接書き込まれたり、ストリームに直接読み込まれたりします.
NIOライブラリでは、すべてのデータがバッファで処理されます.データを読み込むと、バッファに直接読み込まれます.データの書き込み時にバッファに書き込まれます.
バッファは実質的に配列であるが,配列だけではない.バッファは、データへの構造的なアクセスを提供し、システムの読み取り/書き込みプロセスを追跡することもできます.
バッファには次のタイプがあります.
  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

  • じょうたいへんすう
  • capacity:最大データ容量
  • position:現在読み書きされているバイト数、次の要素
  • を指す
  • limit:読み書き可能なバイト数.

  • ファイルNIOインスタンス
    NIOを使用してファイルをすばやくコピーする例を以下に示します.
    public static void fastCopy(String src, String dist) throws IOException {
        /*             */
        FileInputStream fin = new FileInputStream(src);
        /*              */
        FileChannel fcin = fin.getChannel();
        /*              */
        FileOutputStream fout = new FileOutputStream(dist);
        /*              */
        FileChannel fcout = fout.getChannel();
        /*        1024     */
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
        while (true) {
            /*                 */
            int r = fcin.read(buffer);
            /* read()    -1    EOF */
            if (r == -1) {
                break;
            }
            /*      */
            buffer.flip();
            /*                */
            fcout.write(buffer);
            /*       */
            buffer.clear();
        }
    }

    セレクタSelectors
    NIOはしばしば非ブロックIOと呼ばれ、IO多重化におけるReactorモデルを実現し、1つのスレッドThreadは1つのセレクタSelectorを用いてポーリングによって複数のチャネルチャネルチャネルチャネルチャネル上のイベントをリスニングし、1つのスレッドが複数のイベントを処理できるようにする.
    リスニングされたチャネルチャネルチャネルが非ブロッキングであるように構成することにより、チャネル上のIOイベントがまだ到着していない場合、ブロッキング状態になって待機するのではなく、他のチャネルをポーリングし続け、IOイベントが到着したチャネル実行を見つける.
    スレッドの作成と切り替えのオーバーヘッドが大きいため、1つのスレッドではなく複数のイベントを処理するために1つのスレッドを使用し、IOセット型のアプリケーションではパフォーマンスが優れています.
    なお、ソケットチャネルのみが非ブロックに構成できるが、FileChannelはできず、FileChannelに非ブロックを構成しても意味がない.
    1.Selectorオブジェクトの作成
    Selector selector = Selector.open();

    2.Selectorにチャネルチャネルを登録する
    ServerSocketChannel ssc = ServerSocketChannel.open();
    //        
    ssc.configureBlocking(false);
    //         
    ServerSocket ss = ssc.socket();
    InetSocketAddress address = new InetSocketAddress( ports[i] );
    ss.bind( address );	
    // Selector            
    ssc.register(selector, SelectionKey.OP_ACCEPT);

    チャネルをセレクタに登録する場合は、登録する特定のイベントを指定する必要があります.主に次のクラスがあります.
  • SelectionKey.OP_CONNECT
  • SelectionKey.OP_ACCEPT
  • SelectionKey.OP_READ
  • SelectionKey.OP_WRITE

  • SelectionKeyの定義は次のとおりです.
    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;

    各イベントは、イベントセットの整数を構成するビットドメインとして使用できることがわかります.例:
    int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

    3.Selectorを呼び出すselect()メソッド
    // 3.     
    try {    
        while(true) { 
            //       ,           、     
            selector.select(); 
            //         ,           
            Set<SelectionKey> keys = selector.selectedKeys(); 
            Iterator<SelectionKey> iter = keys.iterator(); 
            while (iter.hasNext()) { 
                SelectionKey key = (SelectionKey) iter.next(); 
                iter.remove(); 
                process(key); 
            }    
        }    
    } catch (IOException e) {    
        e.printStackTrace();   
    }

    を選択します.
    操作領域向け
    データバイトストリーム&文字ストリームの処理
    IOブロック/非ブロック
    Java IO
    最初のデータ・ソースに直接適用
    -読み込みごと=すべてのバイト/文字を読み込み、キャッシュなし-読み込みストリームのデータを前後に移動できません
    ブロック-1つのスレッドが読み書き/書き込み時:データが完全に読み書きされる前に&データが準備されていない場合、スレッドは他のタスクを行うことができず、データが準備されてから読み取り/書き込みを継続するしかありません.つまり、ブロック-スレッドがアクティブになったとき&外部が準備されていない場合、ブロック
    Java NIO
    バッファに向かう
    -キャッシュ領域にデータを読み込みます.キャッシュ領域内でストリームデータを前後に移動できます.
    非ブロック-1つのスレッドがあるチャネルに要求を送信し、読み取り/書き込みを要求する場合、データが完全に読み取り/書き込みされる前に&データが準備されていない場合、スレッドは他のタスク(他のチャネルを制御する)を行うことができ、データが準備されてからそのチャネルに切り替え、読み取り/書き込みを継続します.すなわち、選択(Selector)の使用-外部準備ができている場合にスレッドを起動すると、ブロックされません.
    ソケットNIOの例
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.Socket;
    
    public class NIOClient {
        public static void main(String[] args) throws IOException {
            Socket socket = new Socket("127.0.0.1", 8888);
            OutputStream out = socket.getOutputStream();
            String s = "hello world";
            out.write(s.getBytes());
            out.close();
        }
    }
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    
    public class NIOServer {
        public static void main(String[] args) throws IOException {
    
            Selector selector = Selector.open();
    
            ServerSocketChannel ssChannel = ServerSocketChannel.open();
            ssChannel.configureBlocking(false);
            ssChannel.register(selector, SelectionKey.OP_ACCEPT);
    
            ServerSocket serverSocket = ssChannel.socket();
            InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
            serverSocket.bind(address);
    
            while (true) {
    
                selector.select();
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = keys.iterator();
    
                while (keyIterator.hasNext()) {
    
                    SelectionKey key = keyIterator.next();
    
                    if (key.isAcceptable()) {
    
                        ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel();
    
                        //                SocketChannel
                        SocketChannel sChannel = ssChannel1.accept();
                        sChannel.configureBlocking(false);
    
                        //                  
                        sChannel.register(selector, SelectionKey.OP_READ);
    
                    } else if (key.isReadable()) {
    
                        SocketChannel sChannel = (SocketChannel) key.channel();
                        System.out.println(readDataFromSocketChannel(sChannel));
                        sChannel.close();
                    }
    
                    keyIterator.remove();
                }
            }
        }
    
        private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException {
    
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            StringBuilder data = new StringBuilder();
    
            while (true) {
    
                buffer.clear();
                int n = sChannel.read(buffer);
                if (n == -1) {
                    break;
                }
                buffer.flip();
                int limit = buffer.limit();
                char[] dst = new char[limit];
                for (int i = 0; i < limit; i++) {
                    dst[i] = (char) buffer.get(i);
                }
                data.append(dst);
                buffer.clear();
            }
            return data.toString();
        }
    }

    メモリマッピングファイルI/O
    メモリマッピングファイルI/Oは、通常のストリームベースまたはチャネルベースのI/Oよりもはるかに高速でファイルデータの読み取りと書き込みを行う方法です.
    メモリマッピングファイルへの書き込みは危険かもしれませんが、配列の単一要素を変更するという簡単な操作で、ディスク上のファイルが直接変更される可能性があります.データの変更とディスクへのデータの保存は別々ではありません.
    次のコード行は、ファイルの最初の1024バイトをメモリにマッピングし、map()メソッドは、ByteBufferのサブクラスであるMappedByteBufferを返します.したがって、新しいマッピングのバッファは、他の任意のByteBufferを使用するように使用でき、必要に応じてオペレーティングシステムがマッピングを実行します.
    MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);