1. 程式人生 > >【從入門到放棄-Java】併發程式設計-NIO使用

【從入門到放棄-Java】併發程式設計-NIO使用

前言

上文【從入門到放棄-SpringBoot】SpringBoot原始碼分析-請求過程中我們瞭解到,tomcat接收、返回請求的過程都是基於NIO實現的。日常工作中有很多基於NIO的使用,我們知道NIO可以提高系統的併發度,接下來的系列我們來深入學習下NIO,本文先從使用上簡單概述。

NIO概述

NIO即non-blocking(New IO),是指jdk1.4 及以上版本里提供的新api。

NIO和IO最大的區別:IO是以流的方式處理資料,而NIO是以塊的方式處理資料;IO對事件的處理是阻塞的,NIO是非阻塞的

NIO的核心部分:

  • Channel
  • Buffer
  • Selector

NIO主要分為標準輸入輸出和網路請求

標準輸入輸出NIO

讀取

private static void readNio() {
    try {
        //1、開啟檔案讀取流
        FileInputStream fileInputStream = new FileInputStream("/Users/my/Desktop/123.txt");

        //2、獲取fileChannel
        FileChannel channel = fileInputStream.getChannel();

        //3、設定ByteBuffer大小,一次能容納capacity位元組
        int capacity = 9;
        ByteBuffer bf = ByteBuffer.allocate(capacity);

        //4、當read返回-1時,表示檔案讀取完畢
        int length = -1;
        while ((length = channel.read(bf)) != -1) {

            byte[] bytes = bf.array();
            System.out.println(new String(bytes, 0, length));

            //4、將bf position置為0,方便下次讀取
            bf.clear();

        }
        channel.close();
        fileInputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

寫入

private static void writeNio() {
    try {
        //1、開啟檔案寫入流
        FileOutputStream fileOutputStream = new FileOutputStream("/Users/my/Desktop/123.txt");

        //2、獲取fileChannel
        FileChannel channel = fileOutputStream.getChannel();

        //3、初始化byteBuffer
        String str = "薩達案發生大大sdada34;sdds'";
        ByteBuffer bf = ByteBuffer.allocate(1024);

        //4、將bf position置為0,方便下次讀取
        bf.clear();


        //5、從byteBuffer的position位置填充byte
        bf.put(str.getBytes());

        //6、將bf position置為0,limit設定為position避免寫入內容過多
        bf.flip();

        int length = 0;

        //7、如果position小於limit即未寫入完畢
        while (bf.hasRemaining()) {
            //8、將buffer內容寫入channel
            length = channel.write(bf);
            System.out.println(bf);
        }
        channel.close();
        fileOutputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

網路NIO

服務端

package com.my.tools.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
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;

public class ServerSocket {
    private static ServerSocket serverSocket;

    private Selector selector;

    public static void main(String[] args) throws Exception {
        ServerSocket.getInstance().init(8001).listen();
    }

    public static ServerSocket getInstance() {
        if (serverSocket == null) {
            synchronized (ServerSocket.class) {
                if (serverSocket == null) {
                    serverSocket = new ServerSocket();
                }
            }
        }
        return serverSocket;
    }

    public ServerSocket init(int port) throws IOException {
        //初始化channel
        ServerSocketChannel server = ServerSocketChannel.open();

        //繫結本機8001埠
        server.socket().bind(new InetSocketAddress(8001));

        //設定為非阻塞模式
        server.configureBlocking(false);

        //開啟selector管理器
        selector = Selector.open();

        //將selector註冊至server,並設定只處理accept事件
        server.register(selector, SelectionKey.OP_ACCEPT);

        return this;
    }

    public void listen() throws Exception {
        System.out.println("server start");

        //無限迴圈持續監聽
        while (true) {
            //會阻塞 直到監聽到註冊的事件
            selector.select();

            //獲取喚醒的事件
            Iterator<SelectionKey> selectorKeys = selector.selectedKeys().iterator();

            while (selectorKeys.hasNext()) {
                SelectionKey key = selectorKeys.next();

                //將已取出的SelectionKey刪除,防止重複處理
                selectorKeys.remove();

                if (key.isAcceptable()) {

                    //獲取到服務端的socket
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

                    //獲取接收到的客戶端socket
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);

                    //向客戶端寫訊息
                    socketChannel.write(ByteBuffer.wrap(new String("hello, this is server").getBytes()));

                    //註冊監聽read事件
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("accept");
                } else if (key.isReadable()) {
                    //使用selector獲取channel
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    socketChannel.configureBlocking(false);

                    ByteBuffer buffer = ByteBuffer.allocate(1024);

                    //讀訊息
                    int length = socketChannel.read(buffer);

                    String string = new String(buffer.array(), 0 , length);

                    System.out.println("read:" + socketChannel + string);

                    //寫訊息
                    socketChannel.write(ByteBuffer.wrap(("server " + System.currentTimeMillis()).getBytes()));
                    Thread.sleep(10000);
                }
            }
        }
    }

}

客戶端

package com.my.tools.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class ClientSocket {
    public static ClientSocket clientSocket;

    private static Selector selector;

    public static void main(String[] args) throws Exception {
        ClientSocket.getInstance().init("localhost", 8001).listen();
    }

    public static ClientSocket getInstance() {
        if (clientSocket == null) {
            synchronized (ClientSocket.class) {
                if (clientSocket == null) {
                    clientSocket = new ClientSocket();
                }
            }
        }

        return clientSocket;
    }

    public ClientSocket init(String ip, int port) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress(ip, port));
        socketChannel.configureBlocking(false);

        selector = Selector.open();
        socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);

        return this;
    }

    public void listen() throws Exception {
        System.out.println("client start");

        while (true) {
            selector.select();

            Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();

            while (selectionKeys.hasNext()) {
                SelectionKey selectionKey = selectionKeys.next();
                selectionKeys.remove();

                if (selectionKey.isConnectable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);

                    ByteBuffer buffer = ByteBuffer.wrap(new String("hello, this is client").getBytes());
                    socketChannel.write(buffer);

                    socketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("client write");
                } else if (selectionKey.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);

                    ByteBuffer buffer = ByteBuffer.allocate(1024);

                    int length = socketChannel.read(buffer);

                    System.out.println("client read: " + socketChannel + new String(buffer.array(), 0, length));

                    socketChannel.write(ByteBuffer.wrap(("client " + System.currentTimeMillis()).getBytes()));

                    Thread.sleep(10000);
                }
            }

        }
    }
}

總結

上述示例展示了最簡單的檔案NIO和網路NIO用法,接下來會深入分析每個方法的原始碼,並對效能進行調優。

更多文章見:https://nc2era.com

作者:aloof_

原文連結

本文為雲棲社群原創內容,未經