• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • (一)NIO編程之NIO與BIO

    BIO編程

    IO 有的稱之為 basic(基本) IO,有的稱之為 block(阻塞) IO,主要應用于文件 IO 和網絡 IO,
    這里不再說文件 IO, 大家對此都非常熟悉,本次主要講解網絡 IO。
    在 JDK1.4 之前,我們建立網絡連接的時候只能采用 BIO,需要先在服務端啟動一個ServerSocket,然后在客戶端啟動 Socket 來對服務端進行通信,默認情況下服務端需要對每個請求建立一個線程等待請求,而客戶端發送請求后,先咨詢服務端是否有線程響應,如果沒有則會一直等待或者遭到拒絕,如果有的話,客戶端線程會等待請求結束后才繼續執行,這就是阻塞式 IO。
    接下來通過一個例子復習回顧一下 BIO 的基本用法(基于 TCP)。

    package com.bestqiang.bio;
    
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    //BIO 服務器端程序
    public class TCPServer {
        public static void main(String[] args) throws Exception {
            //1.創建 ServerSocket 對象
            ServerSocket ss = new ServerSocket(9999);
            while (true) {
                //2.監聽客戶端
                Socket s = ss.accept(); //阻塞
                //3.從連接中取出輸入流來接收消息
                InputStream is = s.getInputStream(); //阻塞
                byte[] b = new byte[10];
                is.read(b);
                String clientIP = s.getInetAddress().getHostAddress();
                System.out.println(clientIP + "說:" + new String(b).trim());
                //4.從連接中取出輸出流并回話
                OutputStream os = s.getOutputStream();
                os.write("沒錢".getBytes());
                //5.關閉
                s.close();
            }
        }
    }
    

    上述代碼編寫了一個服務器端程序,綁定端口號 9999,accept 方法用來監聽客戶端連接,
    如果沒有客戶端連接,就一直等待,程序會阻塞到這里。

    package com.bestqiang.bio;
    
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.util.Scanner;
    
    //BIO 客戶端程序
    public class TCPClient {
        public static void main(String[] args) throws Exception {
            while (true) {
                //1.創建 Socket 對象
                Socket s = new Socket("127.0.0.1", 9999);
                //2.從連接中取出輸出流并發消息
                OutputStream os = s.getOutputStream();
                System.out.println("請輸入:");
                Scanner sc = new Scanner(System.in);
                String msg = sc.nextLine();
                os.write(msg.getBytes());
                //3.從連接中取出輸入流并接收回話
                InputStream is = s.getInputStream(); //阻塞
                byte[] b = new byte[20];
                is.read(b);
                System.out.println("老板說:" + new String(b).trim());
                //4.關閉
                s.close();
            }
        }
    }
    

    上述代碼編寫了一個客戶端程序,通過 9999 端口連接服務器端,getInputStream 方法用來
    等待服務器端返回數據,如果沒有返回,就一直等待,程序會阻塞到這里。運行效果如下圖
    所示:
    在這里插入圖片描述

    NIO編程

    什么是NIO?

    java.nio 全稱 java non-blocking IO,是指 JDK 提供的新 API。從 JDK1.4 開始,Java 提供了一系列改進的輸入/輸出的新特性,被統稱為 NIO(即 New IO)。新增了許多用于處理輸入輸出的類,這些類都被放在 java.nio 包及子包下,并且對原 java.io 包中的很多類進行改寫,新增了滿足 NIO 的功能。
    在這里插入圖片描述
    NIO 和 BIO 有著相同的目的和作用,但是它們的實現方式完全不同,BIO 以流的方式處理數據,而 NIO 以塊的方式處理數據,塊 I/O 的效率比流 I/O 高很多。另外,NIO 是非阻塞式的,這一點跟 BIO 也很不相同,使用它可以提供非阻塞式的高伸縮性網絡。NIO 主要有三大核心部分:Channel(通道)Buffer(緩沖區), Selector(選擇器)。傳統的 BIO基于字節流和字符流進行操作,而 NIO 基于 Channel(通道)和 Buffer(緩沖區)進行操作,數據總是從通道讀取到緩沖區中,或者從緩沖區寫入到通道中。Selector(選擇區)用于監聽多個通道的事件(比如:連接請求,數據到達等),因此使用單個線程就可以監聽多個客戶端通道。

    文件IO

    概述和核心 API

    緩沖區(Buffer):實際上是一個容器,是一個特殊的數組,緩沖區對象內置了一些機
    制,能夠跟蹤和記錄緩沖區的狀態變化情況。Channel 提供從文件、網絡讀取數據的渠道,
    但是讀取或寫入的數據都必須經由 Buffer,如下圖所示:
    在這里插入圖片描述

    在 NIO 中,Buffer 是一個頂層父類,它是一個抽象類
    在這里插入圖片描述
    常用的 Buffer 子類有:

    • ByteBuffer,存儲字節數據到緩沖區
    • ShortBuffer,存儲字符串數據到緩沖區
    • CharBuffer,存儲字符數據到緩沖區
    • IntBuffer,存儲整數數據到緩沖區
    • LongBuffer,存儲長整型數據到緩沖區
    • DoubleBuffer,存儲小數到緩沖區
    • FloatBuffer,存儲小數到緩沖區

    對于 Java 中的基本數據類型,都有一個 Buffer 類型與之相對應,最常用的自然是ByteBuffer 類(二進制數據),該類的主要方法如下所示:

    • public abstract ByteBuffer put(byte[] b); 存儲字節數據到緩沖區
    • public abstract byte[] get(); 從緩沖區獲得字節數據
    • public final byte[] array(); 把緩沖區數據轉換成字節數組
    • public static ByteBuffer allocate(int capacity); 設置緩沖區的初始容量
    • public static ByteBuffer wrap(byte[] array); 把一個現成的數組放到緩沖區中使用
    • public final Buffer flip(); 翻轉緩沖區,重置位置到初始位置

    通道(Channel):類似于 BIO 中的 stream,例如 FileInputStream 對象,用來建立到目標(文件,網絡套接字,硬件設備等)的一個連接,但是需要注意:BIO 中的 stream 是單向的,例如 FileInputStream 對象只能進行讀取數據的操作,而 NIO 中的通道(Channel)是雙向的,既可以用來進行讀操作,也可以用來進行寫操作。常用的 Channel 類有:
    FileChannel、DatagramChannel、ServerSocketChannel 和 SocketChannel。FileChannel 用于文件的數據讀寫,DatagramChannel 用于 UDP 的數據讀寫,ServerSocketChannel 和 SocketChannel 用于 TCP 的數據讀寫。
    在這里插入圖片描述
    這里我們先講解 FileChannel 類,該類主要用來對本地文件進行 IO 操作,主要方法如下所示:

    • public int read(ByteBuffer dst) ,從通道讀取數據并放到緩沖區中
    • public int write(ByteBuffer src) ,把緩沖區的數據寫到通道中
    • public long transferFrom(ReadableByteChannel src, long position, long count),從目標通道中復制數據到當前通道
    • public long transferTo(long position, long count, WritableByteChannel target),把數據從當前通道復制給目標通道

    下面來使用NIO進行簡單編程,熟悉API基本用法

    package com.bestqiang.nio.file;
    
    import org.junit.Test;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    
    /**
     * @author BestQiang
     */
    // 通過NIO實現文件IO
    public class TestNIO  {
    
        @Test //向本地文件中寫數據
        public void test1() throws Exception {
            //1. 創建輸出流
            FileOutputStream fileOutputStream = new FileOutputStream("basic.txt");
            //2. 從流中得到一個通道
            FileChannel fileChannel = fileOutputStream.getChannel();
            //3. 提供一個緩沖區
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //4. 向緩沖區中存入數據
            String str = "Hello,NIO";
            buffer.put(str.getBytes());
            //** 反轉緩沖區
            buffer.flip();
            //5. 把緩沖區寫到通道中
            fileChannel.write(buffer);
            //6. 關閉
            fileOutputStream.close();
        }
    
        @Test // 從本地文件中讀取數據
        public void test2() throws Exception{
            File file = new File("basic.txt");
            // 1. 創建輸入流
            FileInputStream inputStream = new FileInputStream(file);
            // 2.從流中得到一個通道
            FileChannel channel = inputStream.getChannel();
            // 3.獲取緩沖區
            ByteBuffer buffer = ByteBuffer.allocate((int) file.length());
            // 4.從通道取得數據到緩沖區
            int read = channel.read(buffer);
            System.out.println(new String(buffer.array()));
            inputStream.close();
        }
    
        @Test // 使用NIO實現文件復制
        public void test3() throws Exception{
            // 1.創建兩個流
            FileInputStream inputStream = new FileInputStream("basic.txt");
            FileOutputStream outputStream = new FileOutputStream("basic2.txt");
            // 2.得到兩個通道
            FileChannel inputStreamChannelchannel = inputStream.getChannel();
            FileChannel outputStreamChannelchannel = outputStream.getChannel();
            // 3.復制
            inputStreamChannelchannel.transferTo(0, inputStreamChannelchannel.size(),outputStreamChannelchannel);
    
            inputStream.close();
            outputStream.close();
        }
    }
    
    

    NIO 中的通道是從輸出流對象里通過 getChannel 方法獲取到的,該通道是雙向的,既可以讀,又可以寫。在往通道里寫數據之前,必須通過 put 方法把數據存到 ByteBuffer 中,然后通過通道的 write 方法寫數據。在 write 之前,需要調用 flip 方法翻轉緩沖區,把內部重置到初始位置,這樣在接下來寫數據時才能把所有數據寫到通道里。

    關于buffer.flip()方法,jdk的源碼已經說得很清楚
    在這里插入圖片描述
    上面說翻轉這個緩沖區,將極限位置設置為當前位置,將當前位置設置為0,如果mark被定義,就終止。
    在經過put操作后,應該翻轉緩沖區,然后再進行使用。

    版權聲明:本文為qq_29951485原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_29951485/article/details/90612959

    智能推薦

    網絡編程-BIO、NIO、AIO的原理與對比

    什么是同步異步 同步和異步是針對應用程序和內核交互而言的。同步指的是用戶進程觸發IO操作并等待或者輪詢查看IO操作是否就緒。而異步就是指用戶進程觸發IO操作后便開始干自己的事情,當IO操作完成后,用戶會得到IO完成的通知。 舉個栗子: 同步:自己去銀行取錢。去了銀行申請業務,等待叫號,處理完回家 異步:委托他人代為操作,自己可以干別的,等他人取完錢交給自己。 OS操作系統底層支持異步IO操作。 什...

    BIO NIO

    本篇參考自博客:https://github.com/Snailclimb/JavaGuide/blob/Snailclimb-patch-1/Java%E7%9B%B8%E5%85%B3/BIO%2CNIO%2CAIO%20summary.md 預備知識 在講 BIO,NIO,AIO 之前先來回顧一下這樣幾個概念:同步與異步,阻塞與非阻塞。 同步與異步 (關注的是返回結果這一時間點) 同步: 同...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    猜你喜歡

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    styled-components —— React 中的 CSS 最佳實踐

    https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    精品国产乱码久久久久久蜜桃不卡