どのようにJavaのNIOを習得する方法を教えます

オリジナル:MC(:huakuohao-MCマイクロチャネルパブリック数)がブレース。経験の共有と個人の成長に焦点を当てた大規模なデータ・ベースのJavaプログラミングとフォーカス、。

記事では説明Javaの伝統I/Oである、B-I/O(IOブロッキング)。この記事では説明し何をN-I/O基本的な知識の(非ブロック)、そしてなぜN-I/O多くの利点が高い同時実行中や大きなファイルを処理します。

ローカルファイルI / O操作は、小規模チョッパーを--NIO

チャンネル和バッファー

BIOこれは、運転中であるInputStreamOutputStreamしてNIOいる操作ChannelBuffer我々はできるChannel、鉱物資源を考えるBuffer輸送鉱石車への想像力を。あなたは、データを移動したい場合は、それが助けなければならないBuffer、これはデータを移動するための唯一の方法です。つまりBuffer、今Channel切っても切れない必要があります。

NIO3個までとChannel、それぞれFileChannelSocketChannel同様ServerSocketChannelFileChannel他の二つのネットワークであるが、ローカルファイルを操作するために使用されるI/O操作。

FileChannelここでは、ファイルによってtest-io.tmpファイルへの内容の内部の動きtest-io.md、その使用方法について感じていることChannelBuffer、ファイルの実行I/O動作を制御します。

例:NIOローカルファイルを操作します。

//通过FileInputstream拿到输入FileChannel。
 FileChannel in = new FileInputStream("test-io.tmp").getChannel();
 //通过FileOutPutStream拿到输出FileChannel
FileChannel out = new FileOutputStream("test-io.md").getChannel();
//创建一个字节缓冲器,用于运送数据。
  ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (in.read(buffer) != -1){
            //相当于缓冲器的开关,只有调用该方法,缓冲器里面的数据才能被写入到输出Channel.
            buffer.flip();
            out.write(buffer);
            buffer.clear();
        }
复制代码

容易に達成上記のコード、ファイルtest-io.tmp内のモバイルコンテンツtest-io.md真ん中。

コード読み取り

FileInputStreamオブジェクトgetChannelのメソッドを取得しますChannel

処理(することができる方法)のバッファ容量宣言送信データに使用されるバイト。ByteBufferallocateallocateDirecty1024

ソースデータchannelデータ内をreadバッファ方式に読み込ま。out.write()方法、データは、出力バッファ内部に書き込まれますChannel最後にバッファをクリアし、次の準備のためにデータを読み込みます。

注:データは内部の出力バッファに書き込まれるchannel前に呼び出してください方法を。あなたは開くには、このメソッドの役割を理解することができ、バルブを。バルブを開くためにのみデータが削除されます。bufferflip()Buffer

ByteBuffer

ByteBufferそれはBuffer、サブクラス。など、他の多くのサブクラスがありCharBufferDoubleBufferなどは、ByteBufferほとんどのバッファーで。

私たちはできるByteBufferバイト配列を想像してみてください。おそらく、このような。

図は、カーソルが、各バイトの読み出し、位置移動一つの位置を示すだけ初期化、位置の概略図です。

ByteBuffer次のようにいくつかの重要な方法がありますが、

allocate():たとえば、バッファを作成しますByteBuffer.allocate(1024)allocateDirect():基礎となるオペレーティングシステムに接続された複数のバッファを作成します。capacity():配列のバッファ容量を返します。position():操作するための次の要素の場所。limit():上限の値を返します。flip():オープンバッファのバルブ、読み込み可能。put():バイトは、バッファに格納されます。例えばbyteBuffer.put("hello".getBytes("utf-8")); wrap():バッファストアにバイトのアレイ。例えばByteBuffer.wrap("hello".getBytest()) rewind():位置が0に設定されています。clear():バッファを空にします。hasRemaining()位置値と上限の間に介在されている場合、それは真を返します。

ゼロコピー

上記の例は別の一の実施、コードを見てがあります。

public class ChannelTransfer {
    public static void main(String[] args) throws Exception {
        FileChannel in = new FileInputStream("test-io.tmp").getChannel();
        FileChannel out = new FileOutputStream("test-io.md").getChannel();
        in.transferTo(0,in.size(),out);
        //或者
        //out.transferFrom(in,0,in.size());
    }
}

复制代码

直接オペレーティングシステムのカーネルモードなしの入力端子とドッキングするための出力端子です。これは有名なゼロコピー技術を使用することです。カフカのパフォーマンスの大部分でゼロコピー技術を使用するので、とても不可欠です。

大容量メモリのファイル読み込み

いわゆる大きなファイルを操作するファイルは、あなたが使用することができ、あなたのシステム上で使用可能なメモリよりも大きいですNIO提供するライブラリを操作する次のような方法を。

public static void main(String[] args) throws Exception {
        FileChannel fileChannel = new FileInputStream("test-io.tmp").getChannel();
        //通过map()方法产生一个缓冲器.
        MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());

        if (mappedByteBuffer != null){
            CharBuffer charBuffer = Charset.forName("utf-8").decode(mappedByteBuffer);
            System.out.println(charBuffer.toString());
        }
    }
复制代码

地図()関数は、3つのパラメータそれぞれ、読み書きモード、初期位置とマッピング長さを有することに留意されたいです。私のテストファイルが非常に小さいので、それように、すべてのマップされました。ソースファイルが大きい場合(100G)は、機械の性能に応じて異なる最適値を見つけるために、500Mや1Gあたりにマッピングすることができます。

FileChannelこれらの知識ベースは、と私は皆のために、上記の説明を通じて信じNIOChannelBuffer基本的な理解を持っています。

ネットワークI / O - NIO繁栄。

私たちは言っているNIOノンブロッキングであるI/Oが、前述したFileChanelと非ブロックモードに設定することができない、あなたがおかしい言うことはありません。FileChannel(BIO)は、従来のに比べて、最大の利点は、大容量のファイル、ならびにそのようなゼロコピーなどの技術を使用して処理されます。あなたは基本的な実装原理が何であるかを、実際には、私は知らない私に言わせれば、それだけ知っているFileChannel操作の仕事に応えるために、より体系的な方法で提供された方法の多くを。いわゆる良いお尻ショットは、昇進や賃金の上昇が早く来ます。

あなたが本当に下の基本原理に取得したい場合は、オペレーティングシステムの知識を理解するために移動し、グリル行くことをお勧めしますJDKソースを。

リアルタイムのサポート非ブロッキング操作があるServerSocketChannelSocketChannelネットワーク中の唯一のI/O非ブロッキングI/O強度は遊びの最大の度合いことができます。

あなたはすべての種類を知りたい場合はI/O詳細を私は見ることができますこの記事を

需要提案

私たちは、単純なサーバプログラムを実装すると仮定し、唯一の機能は、クライアントを受信するクライアントに送り返す大文字に変換した後、私の要求、要求の後、内容を送りました。

BIOの実装

クライアントが要求を送信すると、サーバーが処理するためのスレッドを作成するとき。クライアントが同時に100個のスレッドを作成するには、サーバー上で処理されている100件のリクエストを送信する場合。これはかなり良いように見えますが、要求またはときの数が、サービスがあまりにも多くのビットを終わるかもしれない場合は、数千高いがあります。

次の理由の場合:

  1. でも、スレッドプールで、占有システムリソースの作成と破棄スレッド、また根本的に問題を解決することができ、また軽量プロセスで、Linuxのスレッドで
  2. スレッドが無制限作成するために続けることができない、各スレッド内のJavaは512K-1Mメモリ空間を占有します。
  3. あなたは、コンテキストなどを維持したいので、常に、システムリソースを消費しているスレッドの切り替え。
BIOは本当の子供です。

BIOマルチスレッドファッションと無力な項目を選択します。ためSocket.writeSocket.readブロックされています。スレッドの実行が開始されるとことを意味し、いわゆるブロッキング、socket.read操作をした後、あなたは、読み取り操作が完了するのを待つ必要があります。この時間は、データが読まれないことが可能である場合は、これまでのところ、数まで待つ必要があります。これは、BIOそれがあまりにも多くの単純である、方法がない、自然の財産。だから、あなたは、CPUをフルに活用するためにはるかに複数のスレッドを作成したい場合は、スレッドが明るく輝きしない東と呼ばれる別のバーは、常にあり、何のデータではありません。

いくつかの簡単な擬似コードに、我々はそれを少し感じます。

//整个线程池
ExecutorService executor = Executors.newFixedThreadPool(100);
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8888));
//循环监听等待新连接到来
while(true) {
    Socket socket = serverSocket.accept();
    //为新的连接创建新的线程
    executor.submit(new Task(socket));
}
class Task implements Runnable {
    private Socket socket;
    public Task(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        while (!socket.isClosed()) {
            //读数据,阻塞
            String someThing = socket.read();
            if (someThing != null) {
                //处理数据,返回客户端,阻塞
                socket.write();
            }
        }
    }
}
复制代码
NIOは、スマートな子供です。

BIO閉塞上記の読み取りおよび書き込みに問題の嘘。遮断するのでI/O、あまりにもリアル、その結果、死亡およびその他のデータに関するデータが存在しないCPU、完全に利用されていない恥ずかしい状況。比較するとBIONIOそれは単に待つことができないが、データがあるので、あなたは私を知らせ、ちょうどより賢い、私が送っCPUフェッチします。それが取られている場合、速いスピードと呼ばれる完全な左ではなく、少しナンセンスを取ります。CPU(インテリジェンス)動作速度、チャンネル数千を管理する人はものではありません。これは、Reactorプログラミングモデル、また、イベントベースのプログラミングとして知られています。

それはイベントベースのプログラミングであるので、NIOいくつかのイベントより重要であるが、そこにありますReadWriteAcceptConnect

ではNIO、プログラミングモデル、各クライアントは、サーバとの接続が確立しChannelていることChannel、データが通知されます一度CPU、対応するチャンネル番号を取ります。それは次のようになりませんBIOよう、状況は死んスレッドであり、他のデータが発生します。これは、CPU高い理由の使用。

NIOネットワークプログラミング・モデルは、猿王クレマスターのようなビットは、トラブルを診断します。

使用NIOのネットワークプログラミング

上述したように、NIOのネットワークプログラミングは、イベントベースのプログラミングでは、イベントを担当して誰かのリスニングを持っています。この作品Select完成。何か面白いが起こるとき、Selectあなたが知っているようになるだろう。

SelectionKeyまた、非常に重要な役割、同等であるSelectChannelブリッジ。のでSelect面白いが起こった何かを知っているが、またかを知るだけでなく、Channelイベントが何が起こりましたか。

NIO主人公の所与の皆導入内部ネットワークプログラミングは、それぞれ、セレクタ、上でSelector、チャネルServerSocketChannelSocketChanel、同様に上記緩衝液中でByteBufferも、SelectionKey

ここでは上記とエンドサービスプログラムを達成するためにNIOの方法を使用する方法の簡単な解釈をご紹介します。コードを見てください。

public class EchoNioServer {

    public static final int BUF_SIZE = 1024;

    public static void main(String[] args) {

        ByteBuffer byteBuffer = ByteBuffer.allocate(BUF_SIZE);

        try {
            Selector selector = Selector.open();

            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(8888));

            System.out.println("正在8888端口监听...");
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT, byteBuffer);

            while (true) {
                selector.select();
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();

                    if (!key.isValid()) {
                        continue;
                    }

                    if (key.isAcceptable()) {
                        ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = serverSocketChannel1.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ, byteBuffer);

                    } else if (key.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer readBuffer = (ByteBuffer) key.attachment();
                        readBuffer.clear();
                        socketChannel.read(readBuffer);
                        readBuffer.flip();
                        System.out.println("received from client: " + new String(readBuffer.array()).trim());
                        socketChannel.register(selector, SelectionKey.OP_WRITE, readBuffer);

                    } else if (key.isWritable()) {
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer writeBuffer = (ByteBuffer) key.attachment();
                        String msg = new String(writeBuffer.array()).trim().toUpperCase();
                        writeBuffer.clear();
                        writeBuffer.put(msg.getBytes("utf-8"));
                        writeBuffer.flip();
                        socketChannel.write(writeBuffer);
                        writeBuffer.clear();
                        socketChannel.close();
                    }
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
复制代码

コードの解釈は、単純な解釈であることを支援します。理解を容易にするために。

  1. セレクタと予備バッファ、聞いて、データを転送するためのものに興味のあるイベントを作成します。 Selector select = Selector.open(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  2. 作りますServerSocketChannel ServerSocketChannel ssc = ServerSocketChannel.open();
  3. 非ブロックモードに設定されている(非ブロッキングに設定、またはされている必要がありますまたは何NIO) ssc.configureBlocking(false)
  4. ポートバインディング ssc.bind(8888)
  5. チャネルセレクタを登録し、セレクタを伝えるために、私はどのようなこれらのイベントの右に興味があります。イベントは、処理のための適切なロジックを呼び出すために来るとき。sss.register(select,SelectionKey.Accept)
  6. 呼び出しselect.selct()可能なチャネルを見つけるために方法を、この方法は、(真の)CPUのアイドル引き起こすことはありませんしながら、そう言えば、ブロックされています。
  7. さまざまなイベントのために異なる処理を行います。

上記サーバー側のコードのクライアントコードをサポートし、私はあまり説明をしません。

public class EchoNioClient {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            executor.submit(new Task());
        }
        executor.shutdown();
    }
}

class Task implements Runnable {
    InetSocketAddress remoteAddress = new InetSocketAddress(8888);
    static final int BUF_SIZE = 1024;
    @Override
    public void run() {
        try {
            String msg = "hello I'm " + Thread.currentThread().getName();
            SocketChannel socketChannel = SocketChannel.open(remoteAddress);
            ByteBuffer byteBuffer = ByteBuffer.allocate(BUF_SIZE);
            byteBuffer.clear();
            byteBuffer.put(msg.getBytes("utf-8"));
            byteBuffer.flip();
            socketChannel.write(byteBuffer);
            byteBuffer.clear();
            ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
            while (socketChannel.read(receiveBuffer) != -1) {
                receiveBuffer.flip();
                System.out.println("received from server: " + new String(receiveBuffer.array()).trim());
                receiveBuffer.clear();
            }
            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
复制代码
終了

私はこの記事はあなたがより良いNIO基本的なプログラミングを理解するのに役立ちます願っています。あなたが行くとTomcatのソースコードを見ることができたときに退屈基本を理解した後、あなたはまた、それらの大きいことが多い牛ネッティー書き込み高性能ゲートウェイサービスにアップチャットする機会を持つことができます。

最後に、強く自分の手で最高の約、私は都市、書かれたモンゴル円、非現実的な期待が、タブーを見ていない書き込みに、もう一度それを実行し、自分のIDE内のテキストプットの例では、そのことを示唆しています。


推奨読書:

1. これはおそらく、その理由は、製品の性質と相互の涙の開発があるさ

2. どのようにApache httpdの高い同時サービスを実現することです

3. Javaer運用・保守指示コレクション(ファーストフードのバージョン)

・終わり・
 

MCブレース

・ジャワ・ビッグデータ個人的な成長

微信号:huakuohao-mc

おすすめ

転載: juejin.im/post/5e65b430f265da57434bc944