NIO程式設計介紹

女友在高考發表於2022-02-13

I/O模型

java支援3種網路程式設計模型I/O模式:BIO(同步並阻塞)、NIO(同步非阻塞)、AIO(非同步非阻塞)

阻塞指的是訪問IO的執行緒是否會阻塞(或等待)。執行緒訪問資源,該資源是否準備就緒的一種處理方式。

阻塞與非阻塞:

同步與非同步:

1.1 BIO

BIO:同步阻塞,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,可以通過執行緒池機制改善。

程式碼示例:

public class ServerDemo {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket=new ServerSocket(9999);
        ExecutorService executorService = Executors.newCachedThreadPool();
        while (true){
            Socket socket=serverSocket.accept();
            System.out.println("有客戶端連線");
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    InputStream inputStream = null;
                    try {
                        inputStream = socket.getInputStream();
                        byte[] b=new byte[1024];
                        int read = inputStream.read(b);
                        System.out.println("收到客戶端資訊:"+new String(b,0,read));
                        OutputStream outputStream = socket.getOutputStream();
                        outputStream.write("你好客戶端".getBytes(Charset.defaultCharset()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

public class ClientDemo {

    public static void main(String[] args) throws IOException {
        Socket socket=new Socket("127.0.0.1",9999);
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("你好服務端".getBytes(Charset.defaultCharset()));
        InputStream inputStream = socket.getInputStream();
        byte[] bytes=new byte[1024];
        inputStream.read(bytes);
        System.out.println("收到服務端訊息:"+new String(bytes));
        socket.close();

    }
}

BIO問題分析:

  1. 每個請求都需要建立獨立的執行緒,與對應的客戶端進行資料Read、業務處理、資料Write
  2. 併發數較大時,需要建立大量執行緒來處理連線,系統資源佔用較大
  3. 連線建立後,如果當前執行緒暫時沒有資料可讀,則執行緒就阻塞在Read操作上,造成資源阻塞。

1.2 NIO

NIO:同步非阻塞,伺服器實現模式為一個執行緒處理多個請求(連線),即客戶端傳送的連線請求都會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求就進行處理。

程式碼示例:

public class NioSelectorServer {


    public static void main(String[] args) throws IOException, InterruptedException {
        //1.開啟伺服器通道
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        //2.繫結埠號
        serverSocketChannel.bind(new InetSocketAddress(9999));
        //3.設定通道為非阻塞
        serverSocketChannel.configureBlocking(false);
        //4.建立選擇器
        Selector selector = Selector.open();
        //5.將服務端通道註冊到選擇器上,並指定註冊監聽事件為OP_ACCEPT
        serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
        while (true){
            //6.檢查選擇器釋放有事件
//            int select = selector.select(2000);
            int select = selector.select();
            if(select==0){
                System.out.println("無連線");
                continue;
            }
            //7.獲取事件集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                //8.判斷事件是否是連線事件
                if(selectionKey.isAcceptable()){
                    //9.得到客戶端通道,並將通道註冊到選擇器上
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    System.out.println("有客戶端連線");
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector,SelectionKey.OP_READ);
                }
                //10.判斷是否是讀就緒事件
                else if(selectionKey.isReadable()){
                    SocketChannel channel = (SocketChannel)selectionKey.channel();
                    ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                    //11.得到客戶端通道,讀取資料到緩衝區
                    int read = 0;
                    read = channel.read(byteBuffer);
                    System.out.println("客戶端訊息:"+new String(byteBuffer.array()));
                    System.out.println("模擬業務處理中。。。");
                    Thread.sleep(5000);
                    if(read>0){
                        //12.回寫資料給客戶端
                        channel.write(ByteBuffer.wrap("你好客戶端".getBytes(StandardCharsets.UTF_8)));
                        channel.close();
                    }
                }
                //13.從集合刪除對應事件,防止二次處理
                iterator.remove();
            }
        }
    }
}

1.3 AIO

AIO引入了非同步通道的概念,採用了Proactor模式,簡化了程式編寫。它的特點是先由作業系統完成後才通知服務端程式啟動執行緒去處理,一般適用於連線數較多且連線時間比較長的應用。

Proactor模式是一個訊息非同步通知的設計模式,Proactor通知的部署就緒事件,而是操作完成事件。

適用場景

  1. BIO適用於連線數比較小且固定的架構,這種方式對伺服器資源要求比較高
  2. NIO適用於連線數目多且比較短的架構,比如聊天伺服器,彈幕系統,服務期間通訊等
  3. AIO適用於連線數多且連線比較長的架構,比如相簿伺服器。

NIO程式設計

2.1 NIO介紹

Java NIO是JDK提供的新API。NIO有三個核心部分:Channel,Buffer,Selector。NIO是面向緩衝區程式設計的。JAVA NIO的非阻塞模式,是一個執行緒從某個通道傳送或讀取資料,僅能得到目前可用的資料,如果目前沒有資料可用時,就什麼都不會獲取,而不是阻塞執行緒。

通俗理解:NIO是可以做到用一個執行緒來處理多個操作的。假設有1000個請求過來,根據實際情況,可以分配50或100個執行緒來處理。不像BIO那樣,非得分配1000個執行緒。

NIO和BIO比較

  1. BIO是以流的方式處理資料,而NIO是以緩衝區的方式處理資料
  2. BIO是阻塞的,NIO是非阻塞的
  3. BIO基於位元組流和字元流操作,而NIO是基於Channel和Buffer進行操作。NIO基於Selector監聽多個通道的事件。

相關文章