站長資訊網(wǎng)
        最全最豐富的資訊網(wǎng)站

        帶你完全掌握J(rèn)ava NIO(總結(jié)分享)

        本篇文章給大家?guī)砹岁P(guān)于java的相關(guān)知識,其中主要介紹了NIO的相關(guān)問題,包括了NIO核心、BIO與NIO比較、通過NIO實(shí)現(xiàn)簡單的服務(wù)端客戶端通信,希望對大家有幫助。

        帶你完全掌握J(rèn)ava NIO(總結(jié)分享)

        推薦學(xué)習(xí):《java教程》

        一、Java思維導(dǎo)圖

        帶你完全掌握J(rèn)ava NIO(總結(jié)分享)

        二、I/O模型

        I/O模型的本質(zhì)是用什么樣的通道進(jìn)行數(shù)據(jù)的發(fā)送和接收,很大程度上決定了程序通信的性能。
        Java共支持三種網(wǎng)絡(luò)編程模型:BIO、NIO、AIO

        • BIO:同步并阻塞,服務(wù)實(shí)現(xiàn)模式為一個連接一個線程,即客戶端有一個連接請求時,服務(wù)端就需要啟動一個線程進(jìn)行處理。

        • NIO: 同步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個線程處理多個請求連接,即客戶端發(fā)送的請求都會注冊到多路復(fù)用器上,多路復(fù)用器輪詢到連接有I/O請求就進(jìn)行處理。

        • AIO:異步非阻塞,AIO引入異步通道的概念,采用了Proactor模式,簡化了程序編寫,有效的請求才啟動線程,它的特點(diǎn)是先由操作系統(tǒng)完成后才通知服務(wù)端。

        三、BIO、NIO、AIO應(yīng)用場景

        • BIO方式適用于連接數(shù)目比較小且固定的架構(gòu),這種方式對服務(wù)器資源要求比較高, 并發(fā)局限于應(yīng)用中,JDK1.4以前的唯一選擇,但程序簡單易理解。

        • NIO方式適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu),比如聊天服務(wù)器,彈幕 系統(tǒng),服務(wù)器間通訊等。編程比較復(fù)雜,JDK1.4開始支持。

        • AIO方式使用于連接數(shù)目多且連接比較長(重操作)的架構(gòu),比如相冊服務(wù)器,充分 調(diào)用OS參與并發(fā)操作,編程比較復(fù)雜,JDK7開始支持

        四、BIO編程簡單流程

        • 服務(wù)器端啟動一個ServerSocket;

        • 客戶端啟動Socket對服務(wù)器進(jìn)行通 信,默認(rèn)情況下服務(wù)器端需要對每 個客戶 建立一個線程與之通訊;

        • 客戶端發(fā)出請求后, 先咨詢服務(wù)器 是否有線程響應(yīng),如果沒有則會等 待,或者被拒絕;

        • 如果有響應(yīng),客戶端線程會等待請 求結(jié)束后,在繼續(xù)執(zhí)行;

        五、NIO核心

        NIO 有三大核心部分:Selector(選擇器)、Channel(通道)、Buffer(緩沖區(qū))。
        NIO是面向緩沖區(qū),或者說面向塊編程,數(shù)據(jù)讀取到一個 它稍后處理的緩沖區(qū),需要時可在緩沖區(qū)中前后移動,這就 增加了處理過程中的靈活性,使用它可以提供非阻塞式的高伸縮性網(wǎng)絡(luò)。
        HTTP2.0使用了多路復(fù)用的技術(shù),做到同一個連接并發(fā)處理多個請求,而且并發(fā)請求 的數(shù)量比HTTP1.1大了好幾個數(shù)量級。
        簡而言之,NIO可以一個線程處理多個請求。

        六、BIO與NIO比較

        • BIO 以流的方式處理數(shù)據(jù),而 NIO 以塊的方式處理數(shù)據(jù),塊 I/O 的效率比流 I/O 高很多;

        • BIO 是阻塞的,NIO 則是非阻塞的;

        • BIO基于字節(jié)流和字符流進(jìn)行操作,而 NIO 基于 Channel(通道)和 Buffer(緩沖區(qū))進(jìn) 行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū)中,或者從緩沖區(qū)寫入到通道中。Selector(選擇器)用于監(jiān)聽多個通道的事件(比如:連接請求,數(shù)據(jù)到達(dá)等),因 此使用單個線程就可以監(jiān)聽多個客戶端通道。

        七、NIO 三大核心原理示意圖

        帶你完全掌握J(rèn)ava NIO(總結(jié)分享)
        流程圖說明:

        • Selector 對應(yīng)一個線程, 一個線程對應(yīng)多個channel(連接);

        • 該圖反應(yīng)了有三個channel 注冊到 該selector //程序;

        • 每個channel 都會對應(yīng)一個Buffer;

        • 程序切換到哪個channel 是有事件決定的, Event 就是一個重要的概念;

        • Selector 會根據(jù)不同的事件,在各個通道上切換;

        • Buffer 就是一個內(nèi)存塊 , 底層是有一個數(shù)組;

        • 數(shù)據(jù)的讀取寫入是通過Buffer, 這個和BIO , BIO 中要么是輸入流,或者是 輸出流, 不能雙向,但是NIO的Buffer 是可以讀也可以寫, 需要 flip 方法切換;

        • channel 是雙向的, 可以返回底層操作系統(tǒng)的情況, 比如Linux , 底層的操作系統(tǒng) 通道就是雙向的;

        八、緩沖區(qū)(buffer)

        緩沖區(qū)本質(zhì)上是一個可以讀寫數(shù)據(jù)的內(nèi)存塊,可以理解成是一個 容器對象(含數(shù)組),該對象提供了一組方法,可以更輕松地使用內(nèi)存塊,,緩沖區(qū)對 象內(nèi)置了一些機(jī)制,能夠跟蹤和記錄緩沖區(qū)的狀態(tài)變化情況。Channel 提供從文件、 網(wǎng)絡(luò)讀取數(shù)據(jù)的渠道,但是讀取或?qū)懭氲臄?shù)據(jù)都必須經(jīng)由 Buffer。
        在 NIO 中,Buffer 是一個頂層父類,它是一個抽象類。

        1、常用Buffer子類一覽

        • ByteBuffer,存儲字節(jié)數(shù)據(jù)到緩沖區(qū);

        • ShortBuffer,存儲字符串?dāng)?shù)據(jù)到緩沖區(qū);

        • CharBuffer,存儲字符數(shù)據(jù)到緩沖區(qū);

        • IntBuffer,存儲整數(shù)數(shù)據(jù)到緩沖區(qū);

        • LongBuffer,存儲長整型數(shù)據(jù)到緩沖區(qū);

        • DoubleBuffer,存儲小數(shù)到緩沖區(qū);

        • FloatBuffer,存儲小數(shù)到緩沖區(qū);

        2、buffer四大屬性

        • mark:標(biāo)記

        • position:位置,下一個要被讀或?qū)懙脑氐乃饕?每次讀寫緩沖區(qū)數(shù)據(jù)時都會改變改值, 為下次讀寫作準(zhǔn)備。

        • limit:表示緩沖區(qū)的當(dāng)前終點(diǎn),不能對緩沖區(qū) 超過極限的位置進(jìn)行讀寫操作。且極限 是可以修改的

        • capacity:容量,即可以容納的最大數(shù)據(jù)量;在緩 沖區(qū)創(chuàng)建時被設(shè)定并且不能改變。

        帶你完全掌握J(rèn)ava NIO(總結(jié)分享)

        3、buffer常用api

        JDK1.4時,引入的api

        • public final int capacity( )//返回此緩沖區(qū)的容量
        • public final int position( )//返回此緩沖區(qū)的位置
        • public final Buffer position (int newPositio)//設(shè)置此緩沖區(qū)的位置
        • public final int limit( )//返回此緩沖區(qū)的限制
        • public final Buffer limit (int newLimit)//設(shè)置此緩沖區(qū)的限制
        • public final Buffer mark( )//在此緩沖區(qū)的位置設(shè)置標(biāo)記
        • public final Buffer reset( )//將此緩沖區(qū)的位置重置為以前標(biāo)記的位置
        • public final Buffer clear( )//清除此緩沖區(qū), 即將各個標(biāo)記恢復(fù)到初始狀態(tài),但是數(shù)據(jù)并沒有真正擦除, 后面操作會覆蓋
        • public final Buffer flip( )//反轉(zhuǎn)此緩沖區(qū)
        • public final Buffer rewind( )//重繞此緩沖區(qū)
        • public final int remaining( )//返回當(dāng)前位置與限制之間的元素?cái)?shù)
        • public final boolean hasRemaining( )//告知在當(dāng)前位置和限制之間是否有元素
        • public abstract boolean isReadOnly( );//告知此緩沖區(qū)是否為只讀緩沖區(qū)

        JDK1.6時引入的api

        • public abstract boolean hasArray();//告知此緩沖區(qū)是否具有可訪問的底層實(shí)現(xiàn)數(shù)組
        • public abstract Object array();//返回此緩沖區(qū)的底層實(shí)現(xiàn)數(shù)組
        • public abstract int arrayOffset();//返回此緩沖區(qū)的底層實(shí)現(xiàn)數(shù)組中第一個緩沖區(qū)元素的偏移量
        • public abstract boolean isDirect();//告知此緩沖區(qū)是否為直接緩沖區(qū)

        帶你完全掌握J(rèn)ava NIO(總結(jié)分享)

        九、通道(channel)

        1、基本介紹

        (1)NIO的通道類似于流

        • 通道可以同時進(jìn)行讀寫,而流只能讀或者只能寫;
        • 通道可以實(shí)現(xiàn)異步讀寫數(shù)據(jù)
        • 通道可以從緩沖讀數(shù)據(jù),也可以寫數(shù)據(jù)到緩沖

        (2)BIO 中的 stream 是單向的,例如 FileInputStream 對 象只能進(jìn)行讀取數(shù)據(jù)的操作,而 NIO 中的通道 (Channel)是雙向的,可以讀操作,也可以寫操作。
        (3)Channel在NIO中是一個接口
        (4)常用的 Channel 類有:FileChannel、 DatagramChannel、ServerSocketChannel 和 SocketChannel。ServerSocketChanne 類似 ServerSocket , SocketChannel 類似 Socket。
        (5)FileChannel 用于文件的數(shù)據(jù)讀寫, DatagramChannel 用于 UDP 的數(shù)據(jù)讀寫, ServerSocketChannel 和 SocketChannel 用于 TCP 的數(shù)據(jù)讀寫。

        2、FileChannel

        FileChannel主要用來對本地文件進(jìn)行 IO 操作,常見的方法有:

        • read,從通道讀取數(shù)據(jù)并放到緩沖區(qū)中

        • write,把緩沖區(qū)的數(shù)據(jù)寫到通道中

        • transferFrom,從目標(biāo)通道 中復(fù)制數(shù)據(jù)到當(dāng)前通道

        • transferTo,把數(shù)據(jù)從當(dāng) 前通道復(fù)制給目標(biāo)通道

        3、關(guān)于Buffer 和 Channel的注意事項(xiàng)和細(xì)節(jié)

        • ByteBuffer 支持類型化的put 和 get, put 放入的是什么數(shù)據(jù)類型,get就應(yīng)該使用 相應(yīng)的數(shù)據(jù)類型來取出,否則可能有 BufferUnderflowException 異常。

        • 可以將一個普通Buffer 轉(zhuǎn)成只讀Buffer。

        • NIO 還提供了 MappedByteBuffer, 可以讓文件直接在內(nèi)存(堆外的內(nèi)存)中進(jìn) 行修改, 而如何同步到文件由NIO 來完成。

        • NIO 還支持 通過多個 Buffer (即 Buffer 數(shù)組) 完成讀寫操作,即 Scattering 和 Gathering。

        十、Selector(選擇器)

        1、基本介紹

        • Java 的 NIO,用非阻塞的 IO 方式。可以用一個線程,處理多個的客戶端連 接,就會使用到Selector(選擇器)。

        • Selector 能夠檢測多個注冊的通道上是否有事件發(fā)生,如果有事件發(fā)生,便獲取事件然 后針對每個事件進(jìn)行相應(yīng)的處理。這樣就可以只用一個單線程去管理多個 通道,也就是管理多個連接和請求。

        • 只有在 連接/通道 真正有讀寫事件發(fā)生時,才會進(jìn)行讀寫,就大大地減少 了系統(tǒng)開銷,并且不必為每個連接都創(chuàng)建一個線程,不用去維護(hù)多個線程。

        • 避免了多線程之間的上下文切換導(dǎo)致的開銷。

        2、selector的相關(guān)方法

        • open();//得到一個選擇器對象

        • select(long timeout);//監(jiān)控所有注冊的通道,當(dāng)其 中有 IO 操作可以進(jìn)行時,將 對應(yīng)的 SelectionKey 加入到內(nèi)部集合中并返回,參數(shù)用來 設(shè)置超時時間

        • selectedKeys();//從內(nèi)部集合中得 到所有的 SelectionKey。

        3、注意事項(xiàng)

        NIO中的 ServerSocketChannel功能類似ServerSocket,SocketChannel功能類 似Socket。

        十一、通過NIO實(shí)現(xiàn)簡單的服務(wù)端客戶端通信

        1、服務(wù)端

        package com.nezha.guor.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;public class NioServer {     private Selector selector;     private ServerSocketChannel serverSocketChannel;     private static final int PORT = 8080;      public NioServer() {         try {             //獲得選擇器             selector = Selector.open();             serverSocketChannel =  ServerSocketChannel.open();             //綁定端口             serverSocketChannel.socket().bind(new InetSocketAddress(PORT));             //設(shè)置非阻塞模式             serverSocketChannel.configureBlocking(false);             //將該ServerSocketChannel 注冊到selector             serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);         }catch (IOException e) {             System.out.println("NioServer error:"+e.getMessage());         }     }      public void listen() {          System.out.println("監(jiān)聽線程啟動: " + Thread.currentThread().getName());         try {             while (true) {                 int count = selector.select();                 if(count > 0) {                     //遍歷得到selectionKey集合                     Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();                     while (iterator.hasNext()) {                         SelectionKey key = iterator.next();                          if(key.isAcceptable()) {                             SocketChannel sc = serverSocketChannel.accept();                             sc.configureBlocking(false);                             sc.register(selector, SelectionKey.OP_READ);                             System.out.println(sc.getRemoteAddress() + " 上線 ");                         }                         //通道發(fā)送read事件,即通道是可讀的狀態(tài)                         if(key.isReadable()) {                             getDataFromChannel(key);                         }                         //當(dāng)前的key 刪除,防止重復(fù)處理                         iterator.remove();                     }                 } else {                     System.out.println("等待中");                 }             }         }catch (Exception e) {             System.out.println("listen error:"+e.getMessage());         }     }      private void getDataFromChannel(SelectionKey key) {         SocketChannel channel = null;         try {             channel = (SocketChannel) key.channel();             ByteBuffer buffer = ByteBuffer.allocate(1024);              int count = channel.read(buffer);             //根據(jù)count的值做處理             if(count > 0) {                 String msg = new String(buffer.array());                 System.out.println("來自客戶端: " + msg);                  //向其它的客戶端轉(zhuǎn)發(fā)消息(排除自己)                 sendInfoToOtherClients(msg, channel);             }         }catch (IOException e) {             try {                 System.out.println(channel.getRemoteAddress() + " 離線了");                 //取消注冊                 key.cancel();             }catch (IOException ex) {                 System.out.println("getDataFromChannel error:"+ex.getMessage());             }         }finally {             try {                 channel.close();             }catch (IOException ex) {                 System.out.println("channel.close() error:"+ex.getMessage());             }         }     }      //轉(zhuǎn)發(fā)消息給其它客戶(通道)     private void sendInfoToOtherClients(String msg, SocketChannel self ) throws  IOException{         System.out.println("服務(wù)器轉(zhuǎn)發(fā)消息中...");         System.out.println("服務(wù)器轉(zhuǎn)發(fā)數(shù)據(jù)給客戶端線程: " + Thread.currentThread().getName());         //遍歷 所有注冊到selector 上的 SocketChannel,并排除 self         for(SelectionKey key: selector.keys()) {             Channel targetChannel = key.channel();              //排除自己             if(targetChannel instanceof  SocketChannel && targetChannel != self) {                 SocketChannel dest = (SocketChannel)targetChannel;                 //將信息存儲到buffer                 ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());                 //將buffer數(shù)據(jù)寫入通道                 dest.write(buffer);             }         }     }      public static void main(String[] args) {         //創(chuàng)建服務(wù)器對象         NioServer nioServer = new NioServer();         nioServer.listen();     }}

        2、客戶端

        package com.nezha.guor.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;import java.util.Scanner;public class NioClient {     private final int PORT = 8080; //服務(wù)器端口     private Selector selector;     private SocketChannel socketChannel;     private String username;      public NioClient() throws IOException {         selector = Selector.open();         socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT));         //設(shè)置非阻塞         socketChannel.configureBlocking(false);         //將channel注冊到selector         socketChannel.register(selector, SelectionKey.OP_READ);         username = socketChannel.getLocalAddress().toString().substring(1);         System.out.println(username + " is ok...");     }      //向服務(wù)器發(fā)送消息     public void sendInfo(String info) {         info = username + " 說:" + info;         try {             socketChannel.write(ByteBuffer.wrap(info.getBytes()));         }catch (IOException e) {             System.out.println("sendInfo error:"+e.getMessage());         }     }      //讀取從服務(wù)器端回復(fù)的消息     public void readInfo() {         try {             int readChannels = selector.select();             if(readChannels > 0) {                 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();                 while (iterator.hasNext()) {                     SelectionKey key = iterator.next();                     if(key.isReadable()) {                         //得到相關(guān)的通道                         SocketChannel sc = (SocketChannel) key.channel();                         //得到一個Buffer                         ByteBuffer buffer = ByteBuffer.allocate(1024);                         //讀取                         sc.read(buffer);                         //把讀到的緩沖區(qū)的數(shù)據(jù)轉(zhuǎn)成字符串                         String msg = new String(buffer.array());                         System.out.println(msg.trim());                     }                 }                 iterator.remove(); //刪除當(dāng)前的selectionKey, 防止重復(fù)操作             } else {                 System.out.println("沒有可以用的通道...");             }         }catch (Exception e) {             System.out.println("readInfo error:"+e.getMessage());         }     }      public static void main(String[] args) throws Exception {         NioClient nioClient = new NioClient();         new Thread() {             public void run() {                 while (true) {                     nioClient.readInfo();                     try {                         Thread.currentThread().sleep(2000);                     }catch (InterruptedException e) {                         System.out.println("sleep error:"+e.getMessage());                     }                 }             }         }.start();          //發(fā)送數(shù)據(jù)給服務(wù)器端         Scanner scanner = new Scanner(System.in);          while (scanner.hasNextLine()) {             nioClient.sendInfo(scanner.nextLine());         }     }}

        3、控制臺輸出

        帶你完全掌握J(rèn)ava NIO(總結(jié)分享)
        帶你完全掌握J(rèn)ava NIO(總結(jié)分享)

        推薦學(xué)習(xí):《java教程》

        贊(0)
        分享到: 更多 (0)
        網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
        主站蜘蛛池模板: 精品国产乱码久久久久久郑州公司| 亚洲av日韩av天堂影片精品| 免费看污污的网站欧美国产精品不卡在线观看 | 影视网欧洲精品| 亚洲精品NV久久久久久久久久| 91精品国产福利在线观看| 国语自产少妇精品视频| 老年人精品视频在线| 亚洲国产精品一区| 国产精品久久久久…| 欧美精品/日韩精品/国产精品| 国自产偷精品不卡在线| 在线涩涩免费观看国产精品| 精品国产欧美一区二区三区成人 | 亚洲精品乱码久久久久久蜜桃不卡 | 日韩精品电影一区亚洲| 北岛玲日韩精品一区二区三区| 国产精品免费大片| 国产精品无码午夜福利| 亚洲精品午夜无码电影网| 免费精品久久久久久中文字幕 | 四虎精品影库4HUTV四虎| 精品人妻伦一二三区久久| 国产精品龙口护士门在线观看| 欧美精品国产一区二区| 青青草国产精品久久久久| 久久久精品一区二区三区| 精品国产免费一区二区三区香蕉| 国产成人精品精品欧美| 囯产精品一品二区三区| 九九精品99久久久香蕉| 欧美精品久久久久久久自慰| 午夜精品久久久久成人| 亚洲国产精品激情在线观看| 亚洲国产精品日韩| 亚洲国产精品无码久久98| 四虎国产精品永免费| 亚洲国产精品久久电影欧美| 精品人伦一区二区三区潘金莲| 8AV国产精品爽爽ⅴa在线观看| 国产成人精品久久一区二区三区|