• 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");
                    }
                }
            }
        }
    }
  • 相关阅读:
    excel数据导入mySql数据库
    springboot-devtools实现项目的自动重启
    Java中List, Integer[], int[]的相互转换
    Postman代码测试工具如何用?
    git提交代码时,Unstaged changes如何过滤.class .log等文件
    Json字符串转map集合
    实现hibernate 的validator校验
    [转载]OpenSSL中文手册之命令行详解(未完待续)
    Openssl ASN.1 说明一 分享
    [转载]Parsing X.509 Certificates with OpenSSL and C
  • 原文地址:https://www.cnblogs.com/half-two-feet/p/8462966.html
Copyright © 2020-2023  润新知