zoukankan      html  css  js  c++  java
  • NIO简单理解

    NIO:新IO,同步的非阻塞IO。

    1.Java NIO 由以下几个核心部分组成:Channels(通道)、Buffers(缓冲区)、Selectors(选择器)

    Channels(通道)

    1.所有的 IO 在NIO 中都从一个Channel 开始。

    Channel用来读取和写入数据,类似于之前的输入流/输出流。

    Channel通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读

    一般不会直接操作通道,内容都是先读到或者写入到Buffers,再通过Buffers完成读写。

     Buffer(缓冲区)

    1.Buffer可以分为ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer

    2.Buffer中有几个状态变量:

    • position: Buffer读取或写入的下一个操作指针,当向Buffer写入数据时此指针会改变,放在写入的最后一个元素之后。比如Buffer中写入了4个位置的数据,则position会指向第五个位置。
    • limit: 在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。写模式下,limit等于Buffer的capacity。
              当读模式时, limit表示你最多能读到多少数据。
    • capacity: 表示Buffer的最大容量,limit<=capacity。此值在分配Buffer时被设置,一般不会更改。

    3.Buffer中的常用方法:

    ByteBuffer.allocate(数量) : 开辟缓存区,初始化大小

    flip()  :此方法将position设置为0,limit放到position位置。用于重设缓存区,以便接下来的输入输出。

    get() :取出当前position的内容

    put(): 在buffer里面写入内容

    示例如下:

     使用FileChannel配合Buffer读取文件,如下所示:

    public class FileChannelRead {
        public static void main(String[] args) throws IOException {
            FileInputStream fileInputStream=new FileInputStream(new File("E:\test2.txt"));
            FileChannel fileChannel=fileInputStream.getChannel();
            //初始化缓冲区大小
            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
            //将文件通道里面的字节读到缓冲区中
            fileChannel.read(byteBuffer);
            //此方法将position置为0,limit放到position位置
            byteBuffer.flip();
            // position<limit时返回true,用于判断是否还有数据。
            while (byteBuffer.hasRemaining()) {
                //读取缓冲区Buffer里面的内容
                System.out.print((char)byteBuffer.get());
            }
            fileChannel.close();
        }
    }

    使用FileChannel配合Buffer写入文件,如下所示:

    public class FileChannelWrite {
    
        public static void main(String[] args) throws IOException {
            FileOutputStream fileOutputStream=new FileOutputStream(new File("E:\test2.txt"),true);
            //获取输出的fileChannel(文件通道)
            FileChannel fileChannel= fileOutputStream.getChannel();
            //开辟缓冲
            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
            String name="lin";
            //向缓冲区写入数据
             byteBuffer.put(name.getBytes());
             //重设缓冲区,准备输出
             byteBuffer.flip();
             //写入内容
             fileChannel.write(byteBuffer);
             fileChannel.close();
             fileOutputStream.close();
             System.out.println("写入成功");
        }
    }

    Selector(选择器)

     1.Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。

    仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道。对于操作系统来说,线程之间上下文切换的开销很大,而且每个线程都要占用系统的一些资源(如内存)。

    使用Selector配合ServerSocketChannel,进行网络操作,示例如下:

    /**
     * 创建一个非阻塞的服务器,向客户端返回当前的系统时间
     */
    public class SelectorDemo {
        public static void main(String[] args) throws IOException {
             int ports[]= {  8000,8001,8002,8003,8004 } ;
             Selector selector=Selector.open();
             for(int port : ports)  {
                 //打开服务器套接字通道
                 ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
                 //服务器配置设置为非阻塞
                 serverSocketChannel.configureBlocking(false);
                 ServerSocket serverSocket=serverSocketChannel.socket();
                 InetSocketAddress inetSocketAddress=new InetSocketAddress(port);
                 serverSocket.bind(inetSocketAddress);
                 //注册选择器,相当于使用accept()方法接收
                 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT );
                 System.out.println("服务器运行,在"+port+"端口监听");
             }
    
             int keysAdd;
             //选择一组键,相当的通道已为IO准备就绪
             while ( (keysAdd=selector.select())>0 )  {
                 //取出全部生成的key
                 Set<SelectionKey> selectionKeySet=selector.selectedKeys() ;
                 Iterator<SelectionKey> iterator=selectionKeySet.iterator();
                 //迭代全部的SelectionKey
                 while (iterator.hasNext())  {
                     SelectionKey key=(SelectionKey) iterator.next();
                     //判断客户端是否已经连接上
                     if(key.isAcceptable())  {
                         //通过key获取channel
                         ServerSocketChannel serverSocketChannel=(ServerSocketChannel) key.channel();
                         SocketChannel socketChannel=serverSocketChannel.accept();
                         serverSocketChannel.configureBlocking(false);
                         ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                         byteBuffer.put(( "当前时间为"+new Date() ).getBytes());
                         byteBuffer.flip();
                         socketChannel.write(byteBuffer);
                         socketChannel.close();
                     }
                 }
                 selectionKeySet.clear();
             }
        }
    }

    IO和NIO的区别:

    • IO是多线程的,阻塞的。NIO,是同步的非阻塞IO。
    • IO面向Stream(流),而NIO面向Buffer(缓冲区)
    • IO是多个线程的,不存在Selector。而Java NIO的Selector(选择器)允许一个单独的线程来监视多个Channel(输入通道)

    代码示例见GitHub: 

    https://github.com/firefoxer1992/JavaDemo/tree/master/src/main/java/com/nio

    参考资料: http://ifeve.com/java-nio-all/

  • 相关阅读:
    静态邻接表dijkstra
    最短路径系列【最短路径、哈密顿路等】
    python 给多人发送邮件,且将结果添加为附件
    Excel调换数据位置
    try ... except...,好处是执行失败后,仍然可以继续运行
    制作表头,2种方式
    工资表变工资条,2种方式
    C言语教程第一章: C言语概论 (4)
    从红旗5.0说起——看Linux的内存解决
    红旗Linux桌面4.1文本安装历程图解(二)
  • 原文地址:https://www.cnblogs.com/expiator/p/9778143.html
Copyright © 2011-2022 走看看