NIO

簡介

  • NIO與原來的IO有同樣的作用和目的带膜,但是使用方式完全不同玖雁,NIO支持面向緩存區(qū)的、基于通道的IO操作翰铡。NIO將以更加高效的方式進行文件的讀寫操作。

java NIO 與 IO的主要區(qū)別

IO NIO
面向流 面向緩存區(qū)
阻塞IO(Blocking IO) 非阻塞IO(Non Blocking IO)
(無) 選擇器
  • Channel 負責(zé)傳輸
  • Buffer 負責(zé)存儲

緩存區(qū)存取數(shù)據(jù)的核心方法

  • put() 寫

  • get() 讀

  • flip() 切換模式

緩存區(qū)的四個核心屬性

  • private int mark = -1; //標記唱捣,表示當前position的位置两蟀。可以通過reset()恢復(fù)到mark的位置

  • private int position = 0; //位置震缭,表示緩存區(qū)正在操作數(shù)據(jù)的位置赂毯。

  • private int limit; //界限,表示緩存區(qū)可以操作數(shù)據(jù)的大小拣宰。(limit后數(shù)據(jù)不能進行讀寫)

  • private int capacity; //容量党涕,表示緩存區(qū)的最大存儲數(shù)據(jù)的容量。一旦聲明不能改變巡社。

  • 0 <= mark <= position <= limit <= capacity

public class test1 {

    public static void main(String[] args) {
        test2();
    }
    public static void test2(){
        String str = "abcde";

        //1. 分配一個指定大小的緩沖區(qū)
        ByteBuffer buf = ByteBuffer.allocate(1024);

        System.out.println("----------allocate()---------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        //2. 利用put()存入數(shù)據(jù)到緩沖區(qū)中
        buf.put(str.getBytes());

        System.out.println("----------put()---------");
        System.out.println(buf.position());//5
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        //3.切換讀取數(shù)據(jù)模式
        buf.flip();

        System.out.println("----------put()---------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //4.利用get()
        byte[] dst = new byte[buf.limit()];
        buf.get(dst);
        System.out.println(new String(dst,0,dst.length));

        System.out.println("----------get()---------");
        System.out.println(buf.position());//5
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //5.可重復(fù)讀
        buf.rewind();
        System.out.println("----------rewind()---------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //6.clear():清空緩沖區(qū),但是緩沖區(qū)中的數(shù)據(jù)依然存在膛堤,但是出于“被遺忘”狀態(tài)
        buf.clear();
        System.out.println("----------clear()---------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        //mark:標記,表示記錄當前position的位置晌该》世螅可以通過reset()恢復(fù)到mark位置
    }
}

直接緩沖區(qū)和非直接緩沖區(qū)

非直接緩存區(qū):allocate()方法分配緩存區(qū),將緩存區(qū)建立在JVM的內(nèi)存中(堆朝群,數(shù)組)

直接緩存區(qū):allocateDirect()方法分配直接緩存區(qū)燕耿,將緩存區(qū)建立在物理內(nèi)存中,可以提高效率

  • 非直接緩沖區(qū)


    image.png
  • 直接緩沖區(qū)


    image.png

Channel

  • Channel表示IO源與目標打開的連接姜胖。Channel類似于傳統(tǒng)的“流”誉帅。只不過Channel本身不能直接訪問數(shù)據(jù),Channel只能與Buffer進行交互右莱。
  • 分類:
    • FileChannel

    • SelectableChannel

      • SocketChannel
      • SockeServerChannel
      • DatagramChannel
      • Pipe.SinkChannel
      • Pipe.SourceChannel
  • 獲取通道
  1. Java針對支持通道的類提供了getChannel()方法:
  • 本地IO:
    • FileInputStream/FileOutputStream
    • RamdomAccessFile
  • 網(wǎng)絡(luò)IO:
    • Socket
    • ServerSocket
    • DatagramSocket
  1. 在JDK 1.7中的NIO.2 針對各個通道提供了靜態(tài)方法open()
  2. 在JDK 1.7中的NIO.2 的Files工具類的newByteChannel()
  • 利用通道完成文件的復(fù)制(非直接緩沖區(qū))
public class FileCopy {

    public static void main(String[] args) {
        test();
    }

    public static void test(){
        FileInputStream fis = null;
        FileOutputStream fos = null;

        //獲取通道
        FileChannel inchannel = null;
        FileChannel outChannel = null;

        try {
            fis = new FileInputStream("D:/1.jpg");
            fos = new FileOutputStream("D:/2.jpg");

            inchannel = fis.getChannel();
            outChannel = fos.getChannel();

            //分配指定大小的緩沖區(qū)
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //將通道中的數(shù)據(jù)存入到緩沖區(qū)中
            while (inchannel.read(buf) != -1){
                buf.flip(); //切換到讀數(shù)據(jù)的模式
                //將緩沖區(qū)的數(shù)據(jù)寫入到通道中
                outChannel.write(buf);
                buf.clear();//清空緩存區(qū)
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally{
            if(inchannel != null){
                try {
                    inchannel.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            if(outChannel != null){
                try {
                    outChannel.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            if(fis != null){
                try {
                    fis.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            if(fos != null){
                try {
                    fos.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 使用直接緩沖區(qū)完成文件的復(fù)制(內(nèi)存映射文件)
public class FileCopy2 {

    public static void main(String[] args) {
        test3();
    }

    private static void test3() {

        try {
            FileChannel inChannel = FileChannel.open(Paths.get("D:/1.jpg"), StandardOpenOption.READ);
            FileChannel outChannel = FileChannel.open(Paths.get("D:/3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ,
            StandardOpenOption.CREATE_NEW);

            //內(nèi)存映射文件
            MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
            MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

            //直接對緩沖區(qū)進行數(shù)據(jù)的讀寫操作
            byte[] dst = new byte[inMappedBuf.limit()];
            inMappedBuf.get(dst);
            outMappedBuf.put(dst);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

分散(Scatter)和聚集(Gather)

  • 分散讀妊料恰(Scattering Reads): 將通道中的數(shù)據(jù)分散到多個緩沖區(qū)中
  • 聚集寫入(Gathering Writes): 將多個緩沖區(qū)中的數(shù)據(jù)聚集到通道中
public class FileCopy3 {

    public static void main(String[] args) {
        test4();
    }

    private static void test4() {
        try {
            RandomAccessFile raf1 = new RandomAccessFile("D:/3.txt","rw");

            //1.獲取通道
            FileChannel channel1 = raf1.getChannel();

            //2.分配指定大小的緩沖區(qū)
            ByteBuffer buf1 = ByteBuffer.allocate(1);
            ByteBuffer buf2 = ByteBuffer.allocate(1024);

            //3.分散讀取
            ByteBuffer[] bufs = {buf1,buf2};
            channel1.read(bufs);

            for (ByteBuffer byteBuffer : bufs) {
                byteBuffer.flip();
            }

            //聚集寫入
            RandomAccessFile raf2 = new RandomAccessFile("D:/7.txt","rw");
            FileChannel channel2 = raf2.getChannel();
            channel2.write(bufs);


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

Selector

  • 是SelectableChannel的多路復(fù)用器,用于監(jiān)控SelectableChannel的狀態(tài).它是實現(xiàn)NIO非阻塞的關(guān)鍵慢蜓。
  • fileChannel不能切換成非阻塞式模式亚再。
Snipaste_2021-03-17_16-25-30.png

阻塞式網(wǎng)絡(luò)IO實現(xiàn)

  • 客戶端
public class FileTest {

    public static void main(String[] args) {
        client();
    }

    //客戶端
    private static void client() {

        try {
            //1.獲取通道
            SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("9.236.37.131", 9898));

            FileChannel inChannel = FileChannel.open(Paths.get("D:/1.jpg"), StandardOpenOption.READ);

            //2.分配指定大小的緩沖區(qū)
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //3.讀取本地文件,并發(fā)送到服務(wù)端
            while (inChannel.read(buf) != -1){
                buf.flip();
                sChannel.write(buf);
                buf.clear();
            }
            //單向關(guān)閉自己的輸出流晨抡,并未關(guān)閉連接针余,不寫這個服務(wù)器端并不知道傳輸?shù)膱D片結(jié)束了饲鄙。一直卡在ssChannel.accept()
            sChannel.shutdownOutput();

            //接收服務(wù)器的反饋
            int len = 0;
            while ((len = sChannel.read(buf))!=-1){
                buf.flip();
                System.out.println(new String(buf.array(),0,len));
                buf.clear();
            }

            //4.關(guān)閉通道
            inChannel.close();
            sChannel.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  • 服務(wù)端
public class FileTest2 {

    public static void main(String[] args) {
        server();
    }

    //服務(wù)端
    private static void server() {
        try {
            //1.獲取通道
            ServerSocketChannel ssChannel = ServerSocketChannel.open();

            FileChannel outChannel = FileChannel.open(Paths.get("D:/10.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

            //2.綁定鏈接
            ssChannel.bind(new InetSocketAddress(9898));

            //3.獲取客戶端連接的通道
            SocketChannel sChannel = ssChannel.accept();

            //4.分配指定大小的緩沖區(qū)
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //5.接收客戶端的數(shù)據(jù)凄诞,并保存到本地
            while (sChannel.read(buf)!= -1){
                buf.flip();
                outChannel.write(buf);
                buf.clear();
            }

            //發(fā)送反饋給客戶端
            buf.put("服務(wù)器接收數(shù)據(jù)成功".getBytes());
            buf.flip();
            sChannel.write(buf);
            //6.關(guān)閉通道
            sChannel.close();
            outChannel.close();
            ssChannel.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

非阻塞式網(wǎng)絡(luò)IO實現(xiàn)

  • 客戶端
public class NonBlockClient {

    public static void main(String[] args) {
        client();
    }

    private static void client() {

        try {
            //1.獲取通道
            SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("9.236.37.131", 9898));

            //2.切換非阻塞模式
            sChannel.configureBlocking(false);

            //3.分配指定大小的緩沖區(qū)
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //4.發(fā)送數(shù)據(jù)給服務(wù)器
            Scanner scan = new Scanner(System.in);

            while (scan.hasNext()){
                String str = scan.next();
                buf.put((new Date().toString() + "\n"+str).getBytes());
                buf.flip();
                sChannel.write(buf);
                buf.clear();
            }

            //5.關(guān)閉通道
            sChannel.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 服務(wù)端
public class NonBlockServer {

    public static void main(String[] args) {
        server();
    }

    private static void server() {

        try {
            //1.獲取通道
            ServerSocketChannel ssChannel = ServerSocketChannel.open();

            //2.切換非阻塞模式
            ssChannel.configureBlocking(false);

            //3.綁定鏈接
            ssChannel.bind(new InetSocketAddress(9898));

            //4.獲取選擇器
            Selector selector = Selector.open();

            //5.將通道注冊到選擇器上圆雁,并且指定“監(jiān)聽接收事件”
            //SelectionKey-----OP_ACCEPT,OP_CONNECT,OP_READ,OP_WRITE
            ssChannel.register(selector, SelectionKey.OP_ACCEPT);

            //6.輪詢式的獲取選擇器上已經(jīng)“準備就緒”的事件
            while (selector.select()>0){

                //7.獲取當前選擇器中所有注冊的“選擇鍵(已就緒的監(jiān)聽事件)”
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();

                while (it.hasNext()){
                    //8. 獲取準備“就緒”的事件
                    SelectionKey sk = it.next();

                    //9.判斷具體是什么事件準備就緒
                    if(sk.isAcceptable()){
                        //10. 若“接收就緒”,獲取客戶端連接
                        SocketChannel sChannel = ssChannel.accept();

                        //11.切換非阻塞模式
                        sChannel.configureBlocking(false);

                        //12.將該通道注冊到選擇器上
                        sChannel.register(selector,SelectionKey.OP_READ);
                    }else if(sk.isReadable()){
                        //13. 獲取當前選擇器上“讀就緒”狀態(tài)的通道
                        SocketChannel sChannel = (SocketChannel)sk.channel();

                        //14. 讀取數(shù)據(jù)
                        ByteBuffer buf = ByteBuffer.allocate(1024);

                        int len = 0;
                        while ((len = sChannel.read(buf))>0){
                            buf.flip();
                            System.out.println(new String(buf.array(),0,len));
                            buf.clear();
                        }
                    }

                    //15.取消選擇鍵SelectionKey
                    it.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • DatagramChannel --UDP用法類似
  • Pipe


    Snipaste_2021-03-17_16-54-55.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帆谍,一起剝皮案震驚了整個濱河市伪朽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汛蝙,老刑警劉巖烈涮,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異窖剑,居然都是意外死亡坚洽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門西土,熙熙樓的掌柜王于貴愁眉苦臉地迎上來讶舰,“玉大人,你說我怎么就攤上這事需了√纾” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵肋乍,是天一觀的道長鹅颊。 經(jīng)常有香客問我,道長墓造,這世上最難降的妖魔是什么堪伍? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮觅闽,結(jié)果婚禮上帝雇,老公的妹妹穿的比我還像新娘。我一直安慰自己谱煤,他們只是感情好摊求,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刘离,像睡著了一般室叉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上硫惕,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天茧痕,我揣著相機與錄音,去河邊找鬼恼除。 笑死踪旷,一個胖子當著我的面吹牛曼氛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播令野,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼舀患,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了气破?” 一聲冷哼從身側(cè)響起聊浅,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎现使,沒想到半個月后低匙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡碳锈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年顽冶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片售碳。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡强重,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出团滥,到底是詐尸還是另有隱情竿屹,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布灸姊,位于F島的核電站拱燃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏力惯。R本人自食惡果不足惜碗誉,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望父晶。 院中可真熱鬧哮缺,春花似錦、人聲如沸甲喝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽埠胖。三九已至糠溜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間直撤,已是汗流浹背非竿。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谋竖,地道東北人红柱。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓承匣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锤悄。 傳聞我的和親對象是個殘疾皇子韧骗,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容