NIO學習筆記

NIO區別於IO,是同步非阻塞的。nio直接使用native函數庫,直接分配對外內存,經過DirectByteBuffer對象做爲這塊內存的引用操做,避免了數據在java堆和對外內存間來回複製。java

 概念區分:linux

1.同步:使用同步IO時,Java本身處理IO讀寫。編程

2.異步:使用異步IO時,Java將IO讀寫委託給OS處理,須要將數據緩衝區地址和大小傳給OS,完成後OS通知Java處理(回調)。數組

3.阻塞:使用阻塞IO時,Java調用會一直阻塞到讀寫完成才返回。緩存

4.非阻塞:使用非阻塞IO時,若是不能立馬讀寫,Java調用會立刻返回,當IO事件分發器通知可讀寫時在進行讀寫,不斷循環直到讀寫完成。網絡

BIO:同步且阻塞 NIO:同步非阻塞 AIO:異步非阻塞併發

應用場景:併發鏈接數很少時採用BIO,由於它編程和調試都很是簡單,但若是涉及到高併發的狀況,應選擇NIO或AIO,更好的建議是採用成熟的網絡通訊框架Netty。框架

 

NIO主要組件:dom

Channel  通道,文件數據首先存在於channel中 可經過FileInputStream.getChannel()獲取到channel異步

Buffer   緩衝區,數據能夠從通道讀入緩衝區,也能夠將數據從緩衝區寫到通道中

Selector   用於管理一個或多個通道

 

nio讀取文件示例

 1 public class TestChannel {
 2     public static void main(String[] args) throws IOException {
 3         //讀取到文件
 4         RandomAccessFile f = new RandomAccessFile("C:\\test-nio.txt", "rw");
 5         //獲取channel
 6         FileChannel channel = f.getChannel();
 7         //分配緩存大小
 8         ByteBuffer buffer = ByteBuffer.allocate(1024);
 9         //從channel中讀取數據循環讀取到緩存中
10         int bytesRead = channel.read(buffer);
11         while (bytesRead != -1){
12             System.out.println("before flip:"+buffer);
13             //切換爲讀模式,反轉pos爲0,使下一步get從0開始
14             buffer.flip();
15             System.out.println("after flip:"+buffer);
16             while (buffer.hasRemaining()){
17                 System.out.print((char)buffer.get());
18             }
19             //上一步打印完成後清除緩存內數據,使緩衝區能夠再次被寫入
20             buffer.clear();
21             System.out.println("\nafter clear:"+buffer);
22 
23             bytesRead = channel.read(buffer);
24         }
25         //關閉通道
26         channel.close();29     }
30 }

 

將字符串持久化示例

public class TestWriteChannel {
    public static void main(String[] args) throws IOException {
        RandomAccessFile file = new RandomAccessFile("c:/wriate-test.txt","rw");
        FileChannel fileChannel = file.getChannel();

        String data = "我wjeabcdefghijklmnopqrstuvwxyz"+System.currentTimeMillis();
        System.out.println(data.getBytes().length);
        //注意buffer分配的內存空間要大於data.getBytes().length,否者報BufferOverflowException異常
        ByteBuffer buffer = ByteBuffer.allocate(88);

        buffer.clear();
        buffer.put(data.getBytes());
        //切換爲讀模式 pos=0
        buffer.flip();

        while (buffer.hasRemaining()){
            fileChannel.write(buffer);
        }
        fileChannel.close();
    }
}

 

幾個方法說明:

buffer

flip()  將buffer從寫模式切換爲讀模式,是pos=0 limit=「寫模式時最後的pos「。limit表示此緩衝區中最多能夠讀取的字節數

clear() 清空緩衝區,使pos=0 limit=capaticy

compact()   將全部未讀的數據拷貝到Buffer起始處。而後將position設到最後一個未讀元素正後面。注意和clear區分,clear不處理未讀數據,直接清空

rewind()      將position設回0,因此你能夠重讀Buffer中的全部數據。limit保持不變,仍然表示能從Buffer中讀取多少

mark()/reset()   經過調用Buffer.mark()方法,能夠標記Buffer中的一個特定position。以後能夠經過調用Buffer.reset()方法恢復到這個position

put()     將字節存放到buffer中

get()     從buffer中讀取字節

 

channel

size()  channel關聯的文件的大小

position() 獲取當前的位置,加上參數則是設置位置

truncate() 截取文件,如truncate(1024)截取文件的的前1024字節

force() 將channel中的數據強制寫到磁盤上

 

scatter和gather:分散和聚合

scatter:將channel中的數據讀取到多個buffer中,按byffer數組的順序依次填充buffer

gather:將多個buffer中的數據寫到同一個channel中

 1 public class TestScatterGather {
 2     public static void main(String[] args) throws IOException {
 3         RandomAccessFile raf = new RandomAccessFile("c:/test-nio.txt", "rw");
 4         FileChannel channel = raf.getChannel();
 5 
 6         ByteBuffer header = ByteBuffer.allocate(10);
 7         ByteBuffer body = ByteBuffer.allocate(200);
 8 
 9         ByteBuffer[] bufferArr = {header,body};
10         long x = channel.read(bufferArr);
11 
12         header.flip();
13         while (header.hasRemaining()){
14             System.out.print((char)header.get());
15         }
16         header.clear();
17 
18         System.out.println("============");
19 
20         body.flip();
21         while (body.hasRemaining()){
22             System.out.print((char)body.get());
23         }
24         body.clear();
25     }
26 }

 

FileChannel的transferFrom/transferTo方法可用於複製文件,以下示例:

 1 public class TestTransfer {
 2 
 3     public static void main(String[] args) throws IOException {
 4         Long s = System.currentTimeMillis();
 5         RandomAccessFile fromFile = new RandomAccessFile("C:\\迅雷下載\\linux-lite-3.6-32bit.iso", "rw");
 6         FileChannel fromChannel = fromFile.getChannel();
 7         
 8         RandomAccessFile toFile = new RandomAccessFile("C:\\迅雷下載\\linux-lite-3.6-32bit-bak.iso", "rw");
 9         FileChannel toChannel = toFile.getChannel();
10         
11         fromChannel.transferTo(0, fromChannel.size(), toChannel);
12         //或者
13         //toChannel.transferFrom(fromChannel, 0, fromChannel.size());
14         
15         fromChannel.close();
16         toChannel.close();
17         
18         System.out.println("文件複製消耗時間:"+(System.currentTimeMillis()-s));
19     }
20 }

 

Selector在socket的用法示例

public class TestSelector {
    public static void main(String[] args) throws IOException {
        int port = 9999;
        ServerSocketChannel channel = ServerSocketChannel.open();
        channel.bind(new InetSocketAddress(port));
        channel.configureBlocking(false);

        Selector selector = Selector.open();

        channel.register(selector, SelectionKey.OP_ACCEPT);

        while (true){
            System.out.println("開始監聽...");
            int selNum = selector.select();
            System.out.println("selnum: "+selNum);
            if(selNum == 0){
                continue;
            }
            Set<SelectionKey> keySet = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keySet.iterator();
            while (iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                if(selectionKey.isConnectable()){
                    System.out.println("connectable");
                }
                if(selectionKey.isAcceptable()){
                    System.out.println("acceptable");
                }
                if(selectionKey.isReadable()){
                    System.out.println("readable");
                }
                if(selectionKey.isWritable()){
                    System.out.println("writable");
                }
            }
        }
    }
}
相關文章
相關標籤/搜索