• Java NIO(一)


    同步阻塞IO(Blocking IO):即传统的IO模型。

    同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。

    多路复用IO(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。

    异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。 

    阻塞式I/O模型:

     非阻塞式I/O模型:

     在上图中NIO通过Selector 接受请求连接,如map.put("地址A“, accept),map.put("地址B“,read),map.put("地址C“,write)

    当有请求read/write时,才开始建立Channel 建立请求和wirte 通道,并且通过Buffer获取数据块

    多路复用IO模型(JAVA NIO就是采用此模式)

      在多路复用IO模型中,会有一个线程(Java中的Selector)不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。 (selector 支持对多个socketChannel 监听 ,正因为阻塞I/O只能阻塞一个I/O操作,而I/O复用模型能够阻塞多个I/O操作,所以才叫做多路复用。)

     

    NIO关键类:

    Java NIO 由以下几个核心部分组成:

    • Channels
    • Buffers
    • Selectors

    Channel 和 Buffer

    基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:

    Channel和Buffer有好几种类型。(类似于流input  output stram)下面是JAVA NIO中的一些主要Channel的实现:

    • FileChannel:从文件中读写数据。  
    • DatagramChannel:能通过UDP读写网络中的数据。  
    • SocketChannel:能通过TCP读写网络中的数据。  
    • ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。  
    • FileChannel比较特殊,它可以与通道进行数据交互, 不能切换到非阻塞模式,套接字通道可以切换到非阻塞模式;

    缓冲区 - 本质上是一块可以存储数据的内存,被封装成了buffer对象而已!

    这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, double 和 char。

    缓冲区类型:

    • ByteBuffer  
    • MappedByteBuffer  
    • CharBuffer  
    • DoubleBuffer  
    • FloatBuffer  
    • IntBuffer  
    • LongBuffer  
    • ShortBuffer  

    常用方法:

    • allocate() - 分配一块缓冲区  
    • put() - 向缓冲区写数据
    • get() - 向缓冲区读数据  
    • filp() - 将缓冲区从写模式切换到读模式  
    • clear() - 从读模式切换到写模式,不会清空数据,但后续写数据会覆盖原来的数据,即使有部分数据没有读,也会被遗忘;  
    • compact() - 从读数据切换到写模式,数据不会被清空,会将所有未读的数据copy到缓冲区头部,后续写数据不会覆盖,而是在这些数据之后写数据
    • mark() - 对position做出标记,配合reset使用
    • reset() - 将position置为标记值  

    缓冲区的一些属性:

    • capacity - 缓冲区大小,无论是读模式还是写模式,此属性值不会变;
    • position - 写数据时,position表示当前写的位置,每写一个数据,会向下移动一个数据单元,初始为0;最大为capacity - 1切换到读模式时,position会被置为0,表示当前读的位置
    • limit - 写模式下,limit 相当于capacity 表示最多可以写多少数据,切换到读模式时,limit 等于原先的position,表示最多可以读多少数据。

    选择器:相当于一个观察者,用来监听通道感兴趣的事件,一个选择器可以绑定多个通道;

    通道向选择器注册时,需要指定感兴趣的事件,选择器支持以下事件:

    • SelectionKey.OP_CONNECT
    • SelectionKey.OP_ACCEPT
    • SelectionKey.OP_READ
    • SelectionKey.OP_WRITE 

    Selector

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

    这是在一个单线程中使用一个Selector处理3个Channel的图示:

    要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。

    Selector selector = Selector.open();
    channel.configureBlocking(false);
    SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
    while(true) {
      int readyChannels = selector.select();
      if(readyChannels == 0) continue;
      Set selectedKeys = selector.selectedKeys();
      Iterator keyIterator = selectedKeys.iterator();
      while(keyIterator.hasNext()) {
        SelectionKey key = keyIterator.next();
        if(key.isAcceptable()) {
            // a connection was accepted by a ServerSocketChannel.
        } else if (key.isConnectable()) {
            // a connection was established with a remote server.
        } else if (key.isReadable()) {
            // a channel is ready for reading
        } else if (key.isWritable()) {
            // a channel is ready for writing
        }
        keyIterator.remove();
      }
    }
  • 相关阅读:
    记录锁
    linux多线程同步pthread_cond_XXX条件变量的理解
    设置创建文件掩码遇到的问题
    函数的可重入和线程安全
    sigemptyset,sigfillset,sigaddset,sigdelset,sigismember,sigprocmask,sigpendmask作用
    嵌入式中断服务函数的一些特点
    linux alarm函数解除read write等函数的阻塞
    孤儿进程,僵死进程
    标准IO的缓冲问题
    《实用技巧》——让你的网站变成响应式的3个简单步骤
  • 原文地址:https://www.cnblogs.com/fanBlog/p/12176320.html
Copyright © 2020-2023  润新知