美文网首页
Blocking VS non-blocking IO

Blocking VS non-blocking IO

作者: 愚公300代 | 来源:发表于2016-08-26 10:55 被阅读767次

    IO指Blocking IO

    NIO指new/non-blocking IO

    Blocking IO中,对于每一个连接,创建一个线程处理这个连接的IO事件,连接和处理线程之间有1:1的关系。连接数受到JVM线程数的限制。JVM最大线程数限制参考http://jzhihui.iteye.com/blog/1271122,最大线程数由操作系统支持线程数,java初始堆大小,最大堆大小以及每个线程栈大小决定。

    NIO使用单个selector可处理多个连接。

    IO核心代码

    final Socket clientSocket = socket.accept();

    new Thread(new Runnable() {

    @Override

    public void run() {

    ...

    }

    }).start();

    服务端socket.accept()阻塞直至接受新的connection。

    接受新的connection之后,建立新的线程,处理这个连接,每一个连接都有一个线程处理,连接数和线程数是1:1的关系。客户端并发的线程数由同时存活线程数决定。

    NIO的基础概念

    NIO使用selector-based方法来处理网络数据和事件(The NIO API uses a selector-based approach to handle network events and data)

    ByteBuffer:数据容器(不影响原理的理解,之后再讲)。

    NIO Selectors:Selector is a NIO component that determines if one or more channels are ready for reading and/or。

    选择器:选择器是一个组件,这个组件可以决定此时是否有一个或多个的channels准备好进行读写。由于Selector可以处理多个连接,减轻了IO中的线程负担。

    Channel: A channel represents a connection to an entity capable of performing IO operations such as a file or socket.

    通道:通道是指连向可进行IO操作实体的连接。通道本质上是特殊的连接,特殊在其连接的对象都可进行IO操作。NIO的通道似乎都是双向的。

    Selector的使用,需要5步

    创建一个Selector,使得其它的Channel可以注册到Selector

    在注册Channel时,要说明Selector对此Channel感兴趣的时间

    OP_ACCEPT:socket-accept operations的操作位

    OP_CONNECT:socket-connect

    OP_READ:read operation

    OP_WRITE:write operation

    调用Selector.select()阻塞,指导上述事件发生

    事件发生之后,可以获取到SelectionKey实例,每个实例都包括Channel的引用和实际发生的事件。

    此版本NIO核心代码

    serverChannel.register(selector, SelectionKey.OP_ACCEPT);

    while (true) {

    try {

    selector.select();

    } catch (IOException ex) {

    ex.printStackTrace();

    // handle in a proper way

    break;

    }

    Set readyKeys = selector.selectedKeys();

    Iterator iterator = readyKeys.iterator();

    while (iterator.hasNext()) {

    SelectionKey key = (SelectionKey) iterator.next();

    iterator.remove();

    try {

    if (key.isAcceptable()) {

    ServerSocketChannel server = (ServerSocketChannel)

    key.channel();

    SocketChannel client = server.accept();

    System.out.println("Accepted connection from " +

    client);

    client.configureBlocking(false);

    client.register(selector, SelectionKey.OP_WRITE |

    SelectionKey.OP_READ, ByteBuffer.allocate(100));

    }

    if (key.isReadable()) {

    SocketChannel client = (SocketChannel) key.channel();

    ByteBuffer output = (ByteBuffer) key.attachment();

    client.read(output);

    }

    if (key.isWritable()) {

    SocketChannel client = (SocketChannel) key.channel();

    ByteBuffer output = (ByteBuffer) key.attachment();

    output.flip();

    client.write(output);

    output.compact();

    }

    } catch (IOException ex) {

    key.cancel();

    try {

    key.channel().close();

    } catch (IOException cex) {

    }

    }

    }

    从代码中可以看出此版本NIO需要开发者检查网络事件发生,同时触发相应的处理逻辑。

    NIO2

    NIO允许分发IO操作,当IO操作完成时使用completion handler处理IO事件。completion handler的执行完全由底层系统决定,对开发者隐藏。同时保证了每个channel同时

    只有一个completion handler在执行。

    NIO2核心代码

    channel.read(buffer, buffer,new EchoCompletionHandler(channel));

    当读取的IO完成之后,自动执行Echo CompletionHandler的操作。

    private final class EchoCompletionHandler implements

    CompletionHandler {}

    上述内容讲了

    1. IO和NIO的本质区别:IO的thread-per-connection的本质以及NIO selector-based approch to handle network data and event.

    2. IO实现,NIO1实现以及NIO2实现方式的区别,IO使用accept()阻塞等待连接发生之后,创建新的线程处理此连接;NIO1使用selector对channel进行监听,使用selector.select()阻塞直到某个channel准备好读或写,需要开发者检测事件的发生并提供相应的处理逻辑。NIO1是异步的,selector可监听多个channel,这本质上不同于IO;NIO2同样是基于selector但是NIO2的好处在于允许分发IO,并且提供了complete handler用于在IO完成时,自动执行,这种方法不需要检测事件是否发生,然后使用相应的处理逻辑,这一切由底层实现。


    参考:《netty in action》

    相关文章

      网友评论

          本文标题:Blocking VS non-blocking IO

          本文链接:https://www.haomeiwen.com/subject/zwwhjttx.html