NIO基礎操做

原文連接http://zhhll.icu/2020/05/18/java%E5%9F%BA%E7%A1%80/IO/NIO%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/java

NIO

同步非阻塞數組

阻塞與非阻塞的區別:緩存

  • 阻塞時,在調用結果返回時,當前線程會被掛起,並在獲得結果以後返回
  • 非阻塞時,如不能當即獲得結果,該調用不會阻塞當前線程,調用者須要定時輪詢查看處理狀態

Channel(通道)和Buffer(緩衝區)app

與普通IO的不一樣和關係

  • NIO以塊的方式處理數據,可是IO是以最基礎的字節流的形式去寫入和讀出的dom

  • NIO再也不是和IO同樣用OutputStream和InputStream輸入流的形式來進行處理數據的,可是又是基於這種流的方式,採用了通道和緩衝區的形式進行處理異步

  • NIO的通道是能夠雙向的,IO的流只能是單向的ide

  • NIO的緩衝區(字節數組)還能夠進行分片,能夠創建只讀緩衝區、直接緩衝區和間接緩衝區,只讀緩衝區就是隻能夠讀,直接緩衝區是爲了加快I/O速度,以一種特殊的方式分配其內存的緩衝區工具

  • NIO採用的是多路複用的IO模型,BIO用的是阻塞的IO模型性能

通道的概念

通道是對原I/O包中的流的模擬。到任何目的地的全部數據都必須經過一個Channel對象(通道)。一個Buffer實質上就是一個容器對象。發送給一個通道的全部對象都必須首先放到緩衝區中;從通道中讀取的任何數據都要讀到緩衝區中this

緩衝區的概念

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

  • 在NIO庫中,全部數據都是用緩衝區處理的。在讀取數據時,它是直接讀到緩衝區中的。在寫入數據時,它是寫入到緩衝區的。任什麼時候候訪問NIO中的數據,都須要將它放到緩衝區中

  • 緩衝區實質上是一個數組。一般它是一個字節數組,可是也能夠使用其餘種類的數組。可是一個緩衝區不單單是一個數組,緩衝區提供了對數據的結構化訪問,並且還能夠跟蹤系統的讀/寫進程

ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer

選擇器

Selector是多路複用器,用於同時檢測多個通道的事件以實現異步I/O。

經過一個選擇器來同時對多個套接字通道進行監聽,當套接字通道有可用的事件的時候,通道改成可用狀態,選擇器就能夠實現可用的狀態。

工做原理

客戶端-----》Channel-----》Selector------》keys--狀態改變---》server

Buffer 緩衝區
Channel 通道
Selector 選擇器

Server端建立ServerSocketChannel 有一個Selector多路複用器 輪詢全部註冊的通道,根據通道狀態,執行相關操做

  • Connect 鏈接狀態
  • Accept 阻塞狀態
  • Read 可讀狀態
  • Write 可寫狀態

Client端建立SocketChannel 註冊到Server端的Selector

buffer

  • capacity 緩衝區數組的總長度
  • position 下一個要操做的數據元素的位置
  • limit 緩衝區數組中不可操做的下一個元素的位置,limit<=capacity
  • mark 用於記錄當前position的前一個位置或者默認是0
  • clear/flip/rewind等都是操做limit和position的值來實現重複讀寫的

ByteBuffer

有且僅有ByteBuffer(字節緩衝區)能夠直接與通道交互。

public static void main(String[] args) {
        //生成FileChannel文件通道  FileChannel的操做--> 操做ByteBuffer用於讀寫,並獨佔式訪問和鎖定文件區域


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

        try(FileChannel fileChannel = new FileInputStream(FILE).getChannel();
            FileChannel out = new FileOutputStream("C:\\Users\\sinosoft\\Desktop\\copy.txt").getChannel()
        ){
            // 讀取操做,須要調用allocate顯示分配ByteBuffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            // read以後將數據放入緩衝區
            while (fileChannel.read(byteBuffer) != -1){
                byteBuffer.flip(); // 準備寫入
                out.write(byteBuffer);
                byteBuffer.clear(); // 清空緩存區
            }
        } catch (IOException e){
            throw new RuntimeException("讀取文件失敗",e);
        }
    }

方法說明

rewind()方法是將position設置爲緩衝區的開始位置

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

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

mark()設置mark爲當前position

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

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

內存映射文件

內存映射文件能夠建立和修改那些由於太大而沒法放入內存的文件。

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();

映射文件訪問比標準IO性能高不少

文件鎖定

文件鎖定可同步訪問,文件鎖對其餘操做系統進程可見,由於java文件鎖直接映射到本機操做系統鎖定工具。

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("線程準備寫");
                    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);
            // 會報錯 java.nio.channels.NonWritableChannelException
//            fileChannel.read(byteBuffer);
            System.out.println("主線程準備寫");
            fileChannel.write(byteBuffer);
            fileLock.release();
        }
    }
}



主線程睡眠
線程準備寫
java.io.IOException: 另外一個程序已鎖定文件的一部分,進程沒法訪問。
	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)
主線程準備寫

經過調用FileChannel上的tryLock或lock,能夠得到整個文件的FileLock(SocketChannel、DatagramChannel和ServerSocketChannel不須要鎖定,由於本質上就是單線程實體)

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

lock()會阻塞,直到得到鎖,或者調用lock()的線程中斷,或者調用lock()方法的通道關閉。

使用FileLock.release()釋放鎖

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

因爲自己的博客百度沒有收錄,博客地址http://zhhll.icu

相關文章
相關標籤/搜索