Java NIO簡單使用

前段時(shí)間在項(xiàng)目組分享了下NIO,現(xiàn)重新整理一下

一.概念

什么是NIO,和傳統(tǒng)IO有什么區(qū)別:
  • java NIO全稱java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,為所有的原始類型(boolean類型除外)提供緩存支持的數(shù)據(jù)容器,使用它可以提供非阻塞式的高伸縮性網(wǎng)絡(luò)。(百度百科)
  • 主要區(qū)別
IO NIO
面向流 緩沖區(qū)
阻塞 非阻塞
-- 多路復(fù)用

阻塞IO和非阻塞IO

https://www.cnblogs.com/aspirant/p/6877350.htmlutm_source=itdadao&utm_medium=referral
這篇博文介紹的比較清楚.

二.核心組件

  • Channels(通道)
  • Buffers(緩沖區(qū))
  • Selectors(選擇器)

三.Buffer(緩沖區(qū))

Java NIO的Buffer用于和NIO通道進(jìn)行交互,緩沖區(qū)本質(zhì)上可以寫入數(shù)據(jù),讀取數(shù)據(jù)的內(nèi)存,這塊內(nèi)存被包裝成Buffer對象

數(shù)據(jù)都是通過通道(Channel)讀入緩沖區(qū),從緩沖區(qū)寫入通道

屬性

Capacity(容量):緩沖區(qū)能夠容納的元素的最大數(shù)量,初始化時(shí)就被設(shè)定
Limit(上界) :緩沖區(qū)的第一個(gè)不能被讀或?qū)懙脑?緩沖區(qū)中現(xiàn)存元素的個(gè)數(shù))
Position(位置):下一個(gè)要被讀或?qū)懙脑厮饕?位置會(huì)自動(dòng)由get()和put()更新
Mark(標(biāo)記):一個(gè)備忘位置

APi

put() 寫數(shù)據(jù)
get() 讀取
flip()翻轉(zhuǎn):將一個(gè)能夠繼續(xù)添加元素的緩沖區(qū)翻轉(zhuǎn)成一個(gè)準(zhǔn)備讀出元素的釋放狀態(tài)
hasRemaining():函數(shù)會(huì)在釋放緩沖區(qū)時(shí)告訴是否已經(jīng)達(dá)到緩沖區(qū)的上界
clear 回到初始狀態(tài)
mark()標(biāo)記, reset()回滾
ByteBuffer.allocate(10); 創(chuàng)建 底層都是數(shù)組
ByteBuffer.allocateDirect(10); 創(chuàng)建 底層不是數(shù)組
wrap(new byte[10]); 傳入byte數(shù)組,使用整個(gè)數(shù)組創(chuàng)建
wrap(new byte[10],2,3); 傳入byte數(shù)組,使用數(shù)組部分創(chuàng)建
compact 將未讀數(shù)據(jù)前移,positom指向前移后數(shù)據(jù)的后一位,limit = capacity;一次沒有讀完,切換到寫模式

四.Channel(通道)

Java nio的通道類似流,但又有不同:既可以從通道中讀取數(shù)據(jù),又可以寫數(shù)據(jù)到通道,但是流的讀寫通常時(shí)單向的通道可以異步去讀寫通道中的數(shù)據(jù)總是要先讀到一個(gè)Buffer,或總是要從一個(gè)Buffer中寫入

主要實(shí)現(xiàn):

FileChannel : 從文件中讀寫數(shù)據(jù)
DatagramChannel: 通過UDP讀寫網(wǎng)絡(luò)數(shù)據(jù)
SocketChannel:通過TCP讀寫網(wǎng)絡(luò)數(shù)據(jù)
ServerSocketChannel:監(jiān)聽新進(jìn)來的tcp連接

通道讀是將數(shù)據(jù)從通道讀到buffer里面;
通道寫是將數(shù)據(jù)從buffer寫入到通道;

五.selector(選擇器)

選擇器是java NIO中能夠檢測一道多個(gè)NIO通道,并能夠知曉通道是否為讀寫事件做好準(zhǔn)備的組件一個(gè)單獨(dú)的線程可以管理多個(gè)channel

selector使用:
  1. 創(chuàng)建selector
Selector selector = Selector.open();
  1. 注冊channel到selector
channel.configureBlocking(false);
SelectionKey key = channel.register(selector,SelectionKey.OP_READ );

與Selector一起使用時(shí),Channel必須處于非阻塞模式下。這意味著不能將 FileChannel與Selector一起使用,因?yàn)镕ileChannel不能切換到非阻塞模式。而套接字通道都可以。

  1. 通道選擇
int count = selector.select();
  1. 訪問“已選擇鍵集(selected key set)”中的就緒通道
Set selectedKeys = selector.selectedKeys(); 

注意register()方法的第二個(gè)參數(shù)。這是一個(gè)“interest集合”,意思是在通過Selector監(jiān)聽Channel時(shí)對什么事件感興趣??梢员O(jiān)聽四種不同類型的事件:
1. Connect
2. Accept
3. Read
4. Write

SelectionKeys

屬性

  • slector() 獲取對象本身
  • channel() 獲取當(dāng)前就緒的通道
  • cancel() 取消注冊事件(通道)
  • interst(int ops)注冊感興趣事件
  • attachment() 附加對象
image.png
/**
 * @ClassName NioServer
 * @Description TODO
 * @Author liuzetian
 * @Date 2019/5/5 17:55 PM
 * @Version 1.0
 **/
public class NioServer {
    private static Selector selector;
    private static ServerSocketChannel serverSocketChannel;
    private static ByteBuffer bf = ByteBuffer.allocate(1024);

    public static void main(String[] args) throws Exception {
        //創(chuàng)建selector
        selector = Selector.open();
        //打開serverSocketChannel通道
        serverSocketChannel = ServerSocketChannel.open();
        //設(shè)置為非阻塞模式
        serverSocketChannel.configureBlocking(false);
        //開啟綁定端口
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));
        //向selector注冊
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            //count為準(zhǔn)備就緒通道的數(shù)量 阻塞
            int count = selector.select();
            //已選擇鍵集
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            //遍歷
            while (it.hasNext()) {
                SelectionKey key = it.next();
                //連接狀態(tài)
                if (key.isAcceptable()) {
                    System.out.println("新連接進(jìn)入");
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel channel = server.accept();
                    channel.configureBlocking(false);
                    channel.register(selector, SelectionKey.OP_READ);

                } else if (key.isReadable()) {
                    //數(shù)據(jù)可讀狀態(tài)
                    System.out.println("讀準(zhǔn)備就緒,開始讀.......................");
                    //獲取SocketChannel 讀取數(shù)據(jù)
                    SocketChannel channel = (SocketChannel) key.channel();
                    System.out.println("客戶端的數(shù)據(jù)如下:");

                    int readLen = 0;
                    bf.clear();
                    StringBuffer sb = new StringBuffer();
                    //注意一定要讀取完數(shù)據(jù)然后關(guān)閉通道,
                    while ((readLen = channel.read(bf)) > 0) {
                        bf.flip();
                        byte[] temp = new byte[readLen];
                        bf.get(temp, 0, readLen);
                        sb.append(new String(temp));
                        bf.clear();
                    }
                    if (-1 == readLen) {
                        channel.close();
                    }
                    System.out.println(sb.toString());
                    //注冊寫事件
                    channel.register(selector, SelectionKey.OP_WRITE);

                } else if (key.isWritable()) {
                    //寫事件
                    SocketChannel channel = (SocketChannel) key.channel();
                    channel.write(ByteBuffer.wrap(("客戶端,已接收到傳送來的數(shù)據(jù)").getBytes()));
                    //注銷寫事件
                    key.cancel();
//                    channel.close();
                }
                it.remove();
            }
        }
    }
}

介紹的比較簡單, 如有錯(cuò)誤, 歡迎指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容