码迷,mamicode.com
首页 > 编程语言 > 详细

java中NIO的介绍

时间:2019-01-11 11:34:37      阅读:220      评论:0      收藏:0      [点我收藏+]

标签:大小   文件流   特定   address   figure   处理   聊天   nts   etl   

  • NIO和IO的区别
  • NIO : new IO
    有的文章说,NIO用到的是块,也就是每次读入较多的数据缓存,因此使用效率比IO要高些。
    IO:面向流,阻塞IO
    NIO:面向缓冲,非阻塞IO,有selector的支持。
    阻塞IO 读写的好处,每次返回都必然是读写完成了,适用于一个线程处理一个连接,且连接处理发送接收数据量较大的情况。
    非阻塞IO 每次读写返回未必是你想要的数据都读写完成了,即不会等待IO真正完成具体操作,因此在解析数据稍微复杂些。但是NIO由于是非阻塞IO,可以在一个线程中处理多个连接,特别适用于同时读写处理多个管道,且每个管道连接的收发数据量不大的情况,比如互联网中的聊天服务器。

    • NIO中重要的是 Channel、Buffer、Selector。

    Channel就如同IO流里面的Stream的概念。从Channel中读入数据到Buffer,或者从Buffer写入数据到Channel中。
    Selector支持可以同时对多个Channel进行管理,任何一个Channel发生读写,Selector都能够知晓,从而做出对应处理。

    • Channel :

      • FileChannel( FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。)、
      • DatagramChannel
      • SocketChannel
      • ServerSocketChannel
    • Buffer :缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
      Buffer有两个模式:读模式 和 写模式。有三个属性,capacity、position、limit。理解了position和limit属性,你就能明白buffer了。

      • capacity
        是表示内存容量,与读写模式无关,大小固定。
      • position
        当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position 会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.
        当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
      • limit
        在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。
        当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)。
        分配Buffer的capacity:CharBuffer buf = CharBuffer.allocate(1024);

      • 向Buffer中写数据:
        从Channel中读取数据写入Buffer中: int bytesRead = inChannel.read(buf);
        从字符串写入到Buffer中: buf.put(127);
        flip方法:flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。
        从buffer中读取数据:int bytesWritten = inChannel.write(buf); byte aByte = buf.get();
        Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。
      • clear()
        position将被设回0,limit被设置成 capacity的值。换句话说,Buffer 被清空了。Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
        如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。
      • compact()
        将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。

      • mark()与reset()方法
        mark标记position的位置,这样即使后续在使用get之后,再使用reset方法,则position又回回到mark时候的位置。

        private static Logger logger1 = Logger.getLogger(useNIO.class);
        public static void testByteBuffer()
        {
        
              RandomAccessFile f;
              try {
                     //创建文件流
                     f = new RandomAccessFile(".\\log\\nio.txt","rw");
                     //从文件流中获取channel
                     FileChannel channel = f.getChannel();
                     //分配1024个字节
                     ByteBuffer buf = ByteBuffer.allocate(1024);
                     int bytes = channel.read(buf);
                     while(bytes !=-1)
                     {
                           //切换buffer状态,从写入状态变为读取
                           buf.flip();
                           logger1.info("position:"+buf.position()+"  limit:"+buf.limit());
                           //标记当前position的位置,用于之后的reset
                           buf.mark();
                           while(buf.hasRemaining())
                           {
                                  logger1.info((char)buf.get());
                                  logger1.info("position:"+buf.position()+"  limit:"+buf.limit());
                           }
                           //position又回到mark时候的位置。
                           buf.reset();
                           logger1.info("after reset position:"+buf.position()+"  limit:"+buf.limit());
                           //将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
                           buf.compact();
                           logger1.info("after compact  position:"+buf.position()+" limit:"+buf.limit());
                           //position将被设回0,limit被设置成 capacity的值。换句话说,Buffer 被清空了。Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
                           buf.clear();
                           logger1.info("after clear position:"+buf.position()+"  limit:"+buf.limit());
                           bytes = channel.read(buf);
                     }
                     f.close();
              }
              catch (FileNotFoundException e) {
                     e.printStackTrace();
              }
              catch (Exception e)
              {
                     e.printStackTrace();
              }
        
        }
    • Selector,用于对多个Channel进行管理使用,大大提高效率的示例:

      private static ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
         public static void testSelector()
         {
                int port = 33333;
      
                Selector selector;
                try
                {
                       ServerSocketChannel server =  ServerSocketChannel.open();
                       server.configureBlocking(false);
                       server.bind(new InetSocketAddress("127.0.0.1", port));
                       selector = Selector.open();
                       server.register(selector, SelectionKey.OP_ACCEPT);
                       logger1.info("[系统消息提示]监听端口" + port);
                       while(true)
                       {
                             try
                             {
                                    selector.select(1000); //可以设置超时时间,防止进程阻塞
                                     Set<SelectionKey> selectionKeys =  selector.selectedKeys();
                                     Iterator<SelectionKey> selectionKeyIte =  selectionKeys.iterator();
                                     while(selectionKeyIte.hasNext()){
                                             SelectionKey selectionKey =  selectionKeyIte.next();
                                             selectionKeyIte.remove();
                                             DoSomeThing(selectionKey,selector);
                                     }
                             }
                             catch(Exception e)
                             {
      
                             }
      
                       }
                }
                catch
                (Exception e)
                {
      
                }
      
         }
         public static void DoSomeThing(SelectionKey selectionKey,Selector  selector)throws IOException
         {
                 ServerSocketChannel server = null;
                 SocketChannel channel = null;
                 String receiveText;
                  int count;
                  if(selectionKey.isAcceptable()){
                         try {
                               server = (ServerSocketChannel)  selectionKey.channel();
                               channel = server.accept();
                               channel.configureBlocking(false);
                               channel.register(selector, SelectionKey.OP_READ);
                         }
                         catch (IOException e)
                         {
                               e.printStackTrace();
                         }
                  }
                  else if(selectionKey.isReadable())
                  { //获取通道对象,方便后面将通道内的数据读入缓冲区
                         channel = (SocketChannel) selectionKey.channel();
                         receiveBuffer.clear();
                         count = channel.read(receiveBuffer); //如果读出来的客户端数据不为空
                         if(count>0)
                         {
                               receiveText = new  String(receiveBuffer.array(),0,count);
                               logger1.info("[系统消息提示]服务器发现["+receiveText+"]new connect"); }
                         else
                         {
                               logger1.info("[系统消息提示]someone disconnect"); 
                         selectionKey.cancel();
                         }
                       }
         }

    java中NIO的介绍

    标签:大小   文件流   特定   address   figure   处理   聊天   nts   etl   

    原文地址:http://blog.51cto.com/ggwhsd/2341370

    (0)
    (0)
       
    举报
    评论 一句话评论(0
    登录后才能评论!
    © 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
    迷上了代码!