NIO学习笔记

NIO区别于IO,是同步非阻塞的。nio直接使用native函数库,直接分配对外内存,通过DirectByteBuffer对象作为这块内存的引用操作,避免了数据在java堆和对外内存间来回复制。

 概念区分:

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主要组件:

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("
after 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");
                }
            }
        }
    }
}
原文地址:https://www.cnblogs.com/half-two-feet/p/8462966.html