溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

NIO基礎(chǔ)操作有哪些

發(fā)布時(shí)間:2021-10-20 14:38:02 來(lái)源:億速云 閱讀:156 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹“NIO基礎(chǔ)操作有哪些”,在日常操作中,相信很多人在NIO基礎(chǔ)操作有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”NIO基礎(chǔ)操作有哪些”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

NIO

同步非阻塞

阻塞與非阻塞的區(qū)別:

  • 阻塞時(shí),在調(diào)用結(jié)果返回時(shí),當(dāng)前線程會(huì)被掛起,并在得到結(jié)果之后返回

  • 非阻塞時(shí),如不能立即得到結(jié)果,該調(diào)用不會(huì)阻塞當(dāng)前線程,調(diào)用者需要定時(shí)輪詢查看處理狀態(tài)

Channel(通道)和Buffer(緩沖區(qū))

與普通IO的不同和關(guān)系

  • NIO以塊的方式處理數(shù)據(jù),但是IO是以最基礎(chǔ)的字節(jié)流的形式去寫入和讀出的

  • NIO不再是和IO一樣用OutputStream和InputStream輸入流的形式來(lái)進(jìn)行處理數(shù)據(jù)的,但是又是基于這種流的方式,采用了通道和緩沖區(qū)的形式進(jìn)行處理

  • NIO的通道是可以雙向的,IO的流只能是單向的

  • NIO的緩沖區(qū)(字節(jié)數(shù)組)還可以進(jìn)行分片,可以建立只讀緩沖區(qū)、直接緩沖區(qū)和間接緩沖區(qū),只讀緩沖區(qū)就是只可以讀,直接緩沖區(qū)是為了加快I/O速度,以一種特殊的方式分配其內(nèi)存的緩沖區(qū)

  • NIO采用的是多路復(fù)用的IO模型,BIO用的是阻塞的IO模型

通道的概念

通道是對(duì)原I/O包中的流的模擬。到任何目的地的所有數(shù)據(jù)都必須通過(guò)一個(gè)Channel對(duì)象(通道)。一個(gè)Buffer實(shí)質(zhì)上就是一個(gè)容器對(duì)象。發(fā)送給一個(gè)通道的所有對(duì)象都必須首先放到緩沖區(qū)中;從通道中讀取的任何數(shù)據(jù)都要讀到緩沖區(qū)中

緩沖區(qū)的概念

  • Buffer是一個(gè)對(duì)象,它包含一些要寫入或者剛讀出的數(shù)據(jù)。在NIO中加入Buffer對(duì)象,在流式IO中,將數(shù)據(jù)直接寫入或者讀到Stream對(duì)象中

  • 在NIO庫(kù)中,所有數(shù)據(jù)都是用緩沖區(qū)處理的。在讀取數(shù)據(jù)時(shí),它是直接讀到緩沖區(qū)中的。在寫入數(shù)據(jù)時(shí),它是寫入到緩沖區(qū)的。任何時(shí)候訪問NIO中的數(shù)據(jù),都需要將它放到緩沖區(qū)中

  • 緩沖區(qū)實(shí)質(zhì)上是一個(gè)數(shù)組。通常它是一個(gè)字節(jié)數(shù)組,但是也可以使用其他種類的數(shù)組。但是一個(gè)緩沖區(qū)不僅僅是一個(gè)數(shù)組,緩沖區(qū)提供了對(duì)數(shù)據(jù)的結(jié)構(gòu)化訪問,而且還可以跟蹤系統(tǒng)的讀/寫進(jìn)程

ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer

選擇器

Selector是多路復(fù)用器,用于同時(shí)檢測(cè)多個(gè)通道的事件以實(shí)現(xiàn)異步I/O。

通過(guò)一個(gè)選擇器來(lái)同時(shí)對(duì)多個(gè)套接字通道進(jìn)行監(jiān)聽,當(dāng)套接字通道有可用的事件的時(shí)候,通道改為可用狀態(tài),選擇器就可以實(shí)現(xiàn)可用的狀態(tài)。

工作原理

客戶端-----》Channel-----》Selector------》keys--狀態(tài)改變---》server

Buffer 緩沖區(qū) Channel 通道 Selector 選擇器

Server端創(chuàng)建ServerSocketChannel 有一個(gè)Selector多路復(fù)用器 輪詢所有注冊(cè)的通道,根據(jù)通道狀態(tài),執(zhí)行相關(guān)操作

  • Connect 連接狀態(tài)

  • Accept 阻塞狀態(tài)

  • Read 可讀狀態(tài)

  • Write 可寫狀態(tài)

Client端創(chuàng)建SocketChannel 注冊(cè)到Server端的Selector

buffer
  • capacity 緩沖區(qū)數(shù)組的總長(zhǎng)度

  • position 下一個(gè)要操作的數(shù)據(jù)元素的位置

  • limit 緩沖區(qū)數(shù)組中不可操作的下一個(gè)元素的位置,limit<=capacity

  • mark 用于記錄當(dāng)前position的前一個(gè)位置或者默認(rèn)是0

  • clear/flip/rewind等都是操作limit和position的值來(lái)實(shí)現(xiàn)重復(fù)讀寫的

ByteBuffer

有且僅有ByteBuffer(字節(jié)緩沖區(qū))可以直接與通道交互。

public static void main(String[] args) {
        //生成FileChannel文件通道  FileChannel的操作--> 操作ByteBuffer用于讀寫,并獨(dú)占式訪問和鎖定文件區(qū)域


        // 寫入文件
        try(FileChannel fileChannel = new FileOutputStream(FILE).getChannel()){
            fileChannel.write(ByteBuffer.wrap("test".getBytes()));
        } catch (IOException e){
            throw new RuntimeException("寫入文件失敗",e);
        }
        // 在文件結(jié)尾寫入
        try(FileChannel fileChannel = new RandomAccessFile(FILE,"rw").getChannel()){
            fileChannel.position(fileChannel.size());//移至文件結(jié)尾
            fileChannel.write(ByteBuffer.wrap("some".getBytes()));
        } catch (IOException e){
            throw new RuntimeException("寫入文件結(jié)尾失敗",e);
        }

        try(FileChannel fileChannel = new FileInputStream(FILE).getChannel();
            FileChannel out = new FileOutputStream("C:\\Users\\sinosoft\\Desktop\\copy.txt").getChannel()
        ){
            // 讀取操作,需要調(diào)用allocate顯示分配ByteBuffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            // read之后將數(shù)據(jù)放入緩沖區(qū)
            while (fileChannel.read(byteBuffer) != -1){
                byteBuffer.flip(); // 準(zhǔn)備寫入
                out.write(byteBuffer);
                byteBuffer.clear(); // 清空緩存區(qū)
            }
        } catch (IOException e){
            throw new RuntimeException("讀取文件失敗",e);
        }
    }
方法說(shuō)明

rewind()方法是將position設(shè)置為緩沖區(qū)的開始位置

get()和put()都會(huì)修改position

get(int)和put(int)都不會(huì)修改position

mark()設(shè)置mark為當(dāng)前position

flip()將寫模式切換為讀模式

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

內(nèi)存映射文件

內(nèi)存映射文件可以創(chuàng)建和修改那些因?yàn)樘蠖鵁o(wú)法放入內(nèi)存的文件。

RandomAccessFile tdat = new RandomAccessFile("test.dat", "rw");
MappedByteBuffer out = tdat.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length);

或者
FileChannel fc = new FileInputStream(new File("temp.tmp")).getChannel();
IntBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY,0, fc.size()).asIntBuffer();

映射文件訪問比標(biāo)準(zhǔn)IO性能高很多

文件鎖定

文件鎖定可同步訪問,文件鎖對(duì)其他操作系統(tǒng)進(jìn)程可見,因?yàn)閖ava文件鎖直接映射到本機(jī)操作系統(tǒng)鎖定工具。

public class FileLockTest {
    private static final String FILE = "C:\\Users\\sinosoft\\Desktop\\剩余工作副本.txt";
    public static void main(String[] args) throws IOException, InterruptedException {
        FileChannel fileChannel = new FileOutputStream(FILE).getChannel();

        // 文件鎖
        FileLock fileLock = fileChannel.tryLock();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                FileChannel fileChannel = null;
                try {
                    fileChannel = new FileOutputStream(FILE).getChannel();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                byteBuffer.put("aqws".getBytes());
                try {
                    System.out.println("線程準(zhǔn)備寫");
                    fileChannel.write(byteBuffer);
                    System.out.println("線程寫完");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        if(fileLock != null){
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            byteBuffer.put("aqwqdqdhwfwihfejfhi".getBytes());
            System.out.println("主線程睡眠");
            Thread.sleep(10000);
            // 會(huì)報(bào)錯(cuò) java.nio.channels.NonWritableChannelException
//            fileChannel.read(byteBuffer);
            System.out.println("主線程準(zhǔn)備寫");
            fileChannel.write(byteBuffer);
            fileLock.release();
        }
    }
}



主線程睡眠
線程準(zhǔn)備寫
java.io.IOException: 另一個(gè)程序已鎖定文件的一部分,進(jìn)程無(wú)法訪問。
	at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
	at sun.nio.ch.FileDispatcherImpl.write(FileDispatcherImpl.java:75)
	at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
	at sun.nio.ch.IOUtil.write(IOUtil.java:65)
	at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:211)
	at com.zhanghe.study.io.nio.FileLockTest$1.run(FileLockTest.java:35)
	at java.lang.Thread.run(Thread.java:745)
主線程準(zhǔn)備寫

通過(guò)調(diào)用FileChannel上的tryLock或lock,可以獲得整個(gè)文件的FileLock(SocketChannel、DatagramChannel和ServerSocketChannel不需要鎖定,因?yàn)楸举|(zhì)上就是單線程實(shí)體)

tryLock()是非阻塞的,試圖獲取鎖,若不能獲取,只是從方法調(diào)用返回

lock()會(huì)阻塞,直到獲得鎖,或者調(diào)用lock()的線程中斷,或者調(diào)用lock()方法的通道關(guān)閉。

使用FileLock.release()釋放鎖

// 鎖定文件的一部分,鎖住size-position區(qū)域。第三個(gè)參數(shù)指定是否共享此鎖
tryLock(long position, long size, boolean shared)

到此,關(guān)于“NIO基礎(chǔ)操作有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI