`
阅读更多

IO API的可伸缩性对Web应用有着极其重要的意义。Java 1.4版以前的API中,阻塞I/O令许多人失望。从J2SE 1.4版本开始,Java终于有了可伸缩的I/O API。本文分析并计算了新旧IO API在可伸缩性方面的差异。Java向Socket写入数据时必须调用关联的OutputStream的write()方法。只有当所有的数据全部写入时,write()方法调用才会返回。倘若发送缓冲区已满且连接速度很低,这个调用可能需要一段时间才能完成。如果程序只使用单一的线程,其他连接就必须等待,即使那些连接已经做好了调用write()的准备也一样。为了解决这个问题,你必须把每一个Socket和一个线程关联起来;采用这种方法之后,当一个线程由于I/O相关的任务被阻塞时,另一个线程仍旧能够运行。

尽管线程的开销不如进程那么大,但是,考虑到底层的操作平台,线程和进程都属于消耗大量资源的程序结构。每一个线程都要占用一定数量的内存,而且除此之外,多个线程还意味着线程上下文的切换,而这种切换也需要昂贵的资源开销。因此,Java需要一个新的API来分离Socket与线程之间过于紧密的联系。

 

java非阻塞IO package

java.nio

java.nio.channels

java.nio.charset

非阻塞IO实现reactor模式,通过事件机制允许应用程序同时监控多个channel以提高性能,这一功能是通过Selector,SelectableChannel和SelectionKey这3个类来实现的。

SelectableChannel代表了可以支持非阻塞IO操作的channel,可以将其注册在Selector上,这种注册的关系SelectionKey这个类来表现。Selector这个类通过select()函数,给应用程序提供了一个可以同时监控多个IO channel的方法:
应用程序通过调用select()函数,让Selector监控注册在其上的多个SelectableChannel,当有channel的IO操作发生时,select()方法就会返回以让应用程序检查channel的状态,并作相应的处理。
下面是JDK 1.4中非阻塞IO的一个例子,这段code使用了非阻塞IO实现了一个time server:

    private static void acceptConnections(int port) throws Exception {
       // 打开一个Selector
       Selector acceptSelector =
           SelectorProvider.provider().openSelector();
       // 创建一个ServerSocketChannel,这是一个SelectableChannel的子类
       ServerSocketChannel ssc = ServerSocketChannel.open();
       // 将其设为non-blocking状态,这样才能进行非阻塞IO操作
       ssc.configureBlocking(false);
       // 给ServerSocketChannel对应的socket绑定IP和端口
       InetAddress lh = InetAddress.getLocalHost();
       InetSocketAddress isa = new InetSocketAddress(lh, port);
       ssc.socket().bind(isa);
       // 将ServerSocketChannel注册到Selector上,返回对应的SelectionKey
       SelectionKey acceptKey =
           ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);
       int keysAdded = 0;
       // 用select()函数来监控注册在Selector上的SelectableChannel
       // 返回值代表了有多少channel可以进行IO操作 (ready for IO)
       while ((keysAdded = acceptSelector.select()) > 0) {
           // selectedKeys()返回一个SelectionKey的集合,
           // 其中每个SelectionKey代表了一个可以进行IO操作的channel。
           // 一个ServerSocketChannel可以进行IO操作意味着有新的TCP连接连入了
           Set readyKeys = acceptSelector.selectedKeys();
           Iterator i = readyKeys.iterator();
           while (i.hasNext()) {
              SelectionKey sk = (SelectionKey) i.next();
              // 需要将处理过的key从selectedKeys这个集合中删除
              i.remove();
              // 从SelectionKey得到对应的channel
              ServerSocketChannel nextReady =
                  (ServerSocketChannel) sk.channel();
              // 接受新的TCP连接
              Socket s = nextReady.accept().socket();
              // 把当前的时间写到这个新的TCP连接中
              PrintWriter out =
                  new PrintWriter(s.getOutputStream(), true);
              Date now = new Date();
              out.println(now);
              // 关闭连接
              out.close();
           }
       }
    }

 

这是个纯粹用于演示的例子,因为只有一个ServerSocketChannel需要监控,所以其实并不真的需要使用到非阻塞IO。不过正因为它的简单,可以很容易地看清楚非阻塞IO是如何工作的。
SelectableChannel
这个抽象类是所有支持非阻塞IO操作的channel(如DatagramChannel、SocketChannel)的父类。SelectableChannel可以注册到一个或多个Selector上以进行非阻塞IO操作。
SelectableChannel可以是blocking和non-blocking模式(所有channel创建的时候都是blocking模式),只有non-blocking的SelectableChannel才可以参与非阻塞IO操作。

Selector中的注册方法:

public abstract class AbstractSelector extends Selector {

    protected abstract SelectionKey register(AbstractSelectableChannel ch,  int ops, Object att);

    。。。

}

 将当前channel注册到一个Selector上并返回对应的SelectionKey。在这以后,通过调用Selector的select()函数就可以监控这个channel。ops这个参数是一个bit mask,代表了需要监控的IO操作。

 

 

Channel中的注册方法:

public abstract class AbstractSelectableChannel extends SelectableChannel{

 

    public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException
    {
 if (!isOpen())
     throw new ClosedChannelException();
 if ((ops & ~validOps()) != 0)
     throw new IllegalArgumentException();
 synchronized (regLock) {
     if (blocking)
  throw new IllegalBlockingModeException();
     SelectionKey k = findKey(sel);
            if (k != null) {
                k.interestOps(ops);
  k.attach(att);
            }
     if (k == null) {
  // New registration
  k = ((AbstractSelector)sel).register(this, ops, att);
  addKey(k);
     }
            return k;
        }
    }

 

。。。

}

通过register()方法,SelectableChannel可以注册到Selector上。

 

总之,就是selectAbleChannel注册到Selector上,可以被selector监控,观察者、事件机制。

 

 

 

 

Selector open() ,是Selector的一个静态方法,用于创建实例。

SelectionKey 代表了Selector和SelectableChannel的注册关系。
在一个Selector中,有3个SelectionKey的集合:
1.key set代表了所有注册在这个Selector上的channel,这个集合可以通过keys()方法拿到。
2.Selected-key set代表了所有通过select()方法监测到可以进行IO操作的channel,这个集合可以通过selectedKeys()拿到。
3.Cancelled-key set代表了已经cancel了注册关系的channel,在下一个select()操作中,这些channel对应的SelectionKey会从key set和cancelled-key set中移走。这个集合无法直接访问。

 

 

Selector定义了4个静态常量来表示4种IO操作,这些常量可以进行位操作组合成一个bit mask。

int OP_ACCEPT
有新的网络连接可以accept,ServerSocketChannel支持这一非阻塞IO。
int OP_CONNECT
代表连接已经建立(或出错),SocketChannel支持这一非阻塞IO。
int OP_READ
int OP_WRITE
代表了读、写操作。

 

java doc:

SocketChannel针对面向流的连接套接字的可选择通道(client),

ServerSocketChannel针对面向流的侦听套接字的可选择通道(server),两者都集成自SelectableChannel。

 

可通过 Selector 实现多路复用的通道。

为了与选择器一起使用,此类的实例必须首先通过 register 方法进行注册。此方法返回一个表示该通道已向选择器注册的新 SelectionKey 对象。

向选择器注册后,通道在注销 之前将保持注册状态。注销涉及释放选择器已分配给该通道的所有资源。

不能直接注销通道;相反,必须取消 表示通道注册的键。取消某个键要求在选择器的下一个选择操作期间注销通道。可通过调用某个键的 cancel 方法显式地取消该键。无论是通过调用通道的 close 方法,还是中断阻塞于该通道上 I/O 操作中的线程来关闭该通道,都会隐式地取消该通道的所有键。

如果选择器本身已关闭,则将注销该通道,并且表示其注册的键将立即无效。

一个通道至多只能在任意特定选择器上注册一次。

可通过调用 isRegistered 方法来确定是否向一个或多个选择器注册了某个通道。

多个并发线程可安全地使用可选择的通道。 isBlocking 方法来确定其阻塞模式。

阻塞模式

可选择的通道要么处于阻塞 模式,要么处于非阻塞 模式。在阻塞模式中,每一个 I/O 操作完成之前都会阻塞在其通道上调用的其他 I/O 操作。在非阻塞模式中,永远不会阻塞 I/O 操作,并且传输的字节可能少于请求的数量,或者可能根本不传输字节。可通过调用可选择通道的

新创建的可选择通道总是处于阻塞模式。在结合使用基于选择器的多路复用时,非阻塞模式是最有用的。向选择器注册某个通道前,必须将该通道置于非阻塞模式,并且在注销之前可能无法返回到阻塞模式。

 

SelectionKey表示 SelectableChannelSelector 中的注册的标记。

每次向选择器注册通道时就会创建一个选择键。通过调用某个键的 cancel 方法、关闭其通道,或者通过关闭其选择器来取消 该键之前,它一直保持有效。取消某个键不会立即从其选择器中移除它;相反,会将该键添加到选择器的已取消键集,以便在下一次进行选择操作时移除它。可通过调用某个键的 isValid 方法来测试其有效性。

 

继续学习

nio与不同内核实现的绑定epoll、iocp ,reactor模式,深入了解原理。

netty, mina实现。

JAVA NIO是针对多连接并发的高效处理,并不一定能够提高单个io的处理速度,技术的适用场景。

 

 

 

分享到:
评论

相关推荐

    java NIO和java并发编程的书籍

    java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...

    JavaNIO chm帮助文档

    Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...

    Java NIO英文高清原版

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java平台中用于替代标准I/O(BIO)模型的一种新机制。NIO在Java 1.4版本引入,提供了更高效的数据处理和通道通信方式,特别适用于高并发、大数据...

    java NIO.zip

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java标准库提供的一种替代传统的I/O模型的新技术。自Java 1.4版本引入NIO后,它为Java开发者提供了更高效的数据传输方式,尤其是在处理大量并发...

    java nio 包读取超大数据文件

    ### Java NIO 处理超大数据文件的知识点详解 #### 一、Java NIO简介 Java NIO(New IO)是Java平台上的新输入/输出流API,它提供了与传统IO(即Java IO)不同的数据处理方式。NIO在Java 1.4版本引入,并在后续版本...

    java NIO 视频教程

    Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...

    Java NIO 中文 Java NIO 中文 Java NIO 中文文档

    Java NIO 深入探讨了 1.4 版的 I/O 新特性,并告诉您如何使用这些特性来极大地提升您所写的 Java 代码的执行效率。这本小册子就程序员所面临的有代表性的 I/O 问题作了详尽阐述,并讲解了 如何才能充分利用新的 I/O ...

    java NIO技巧及原理

    Java NIO(New Input/Output)是Java标准库提供的一种I/O模型,它与传统的 Blocking I/O(IO)相比,提供了更加高效的数据传输方式。在Java NIO中,"新"主要体现在非阻塞和多路复用这两个特性上,这使得NIO更适合于...

    一个java NIO的例子

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java标准库提供的一种替代传统I/O模型的新技术。在传统的Java IO模型中,读写操作是阻塞的,即当调用read或write方法时,线程会等待数据准备好或...

    java NIO实例

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java从1.4版本开始引入的一种新的I/O模型,它为Java应用程序提供了更高效的数据传输方式。传统的Java I/O模型(BIO)在处理大量并发连接时效率较...

    java nio入门学习,两个pdf

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java标准库提供的一种替代传统I/O模型的新技术。在Java 1.4版本中引入,NIO提供了一种全新的I/O编程方式,使得Java开发者能够更高效地处理I/O操作...

    java nio 实现socket

    ### Java NIO 实现Socket通信详解 #### 一、NIO与传统IO的区别及优势 在探讨如何使用Java NIO实现Socket通信之前,我们需要先理解NIO(Non-blocking I/O,非阻塞I/O)与传统阻塞I/O之间的区别。 **传统阻塞I/O...

    Java NIO测试示例

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java从1.4版本开始引入的一种新的I/O模型,它为Java应用程序提供了更高效、灵活的I/O操作方式。NIO与传统的 Blocking I/O(阻塞I/O)模式相比,...

Global site tag (gtag.js) - Google Analytics