博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nio
阅读量:4032 次
发布时间:2019-05-24

本文共 5064 字,大约阅读时间需要 16 分钟。

非阻塞I/O

Java NIO的一个非常重要的特点就是,引入了非阻塞的I/O。其中Selector类是非阻塞I/O的核心类。Selector是一个能够检查多个Channel,并发现哪些Channel已经准备好了数据传送的NIO组件。这样可以在一个线程去管理多个Channel,例如多个网络连接。Selector与 Windows 消息循环类似,它从不同客户机捕获各种事件并将它们分派到相应的事件处理程序。

一个常见的网络 IO 通讯流程如下 :

图 2 网络 IO 通讯流程

 

通讯过程中若连接还没到来,那么accept会阻塞程序运行到这里不得不挂起CPU 转而执行其他线程。同样,read 会一样也会阻塞。

阻塞式网络 IO 的特点:多线程处理多个连接,例如10000个连接就需要10000个线程(消耗资源),并且阻塞的结果就是会带来大量的线频繁地进行上下文切换(消耗时间)。

非阻塞式IO的出现的目的就是为了解决这个瓶颈。而非阻塞式IO是怎么实现的呢?非阻塞IO处理连接的线程数和连接数没有联系,也就是说处理10000个连接非阻塞IO不需要10000个线程,你可以用1000个也可以用2000个线程来处理。因为非阻塞IO处理连接是异步的。当某个连接发送请求到服务器,服务器把这个连接请求当作一个请求“事件”,并把这个"事件"分配给相应的函数处理。我们可以把这个处理函数放到线程中去执行,执行完就把线程归还。这样一个线程就可以异步的处理多个事件。而阻塞式IO的线程的大部分时间都浪费在等待请求上了

图 3 Java NIO: A Thread uses a Selector to handle 3 Channel's

下面是一个非阻塞式网络通信的例子,分为服务器端和客户端程序。

NonBlockingServer.java

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.util.Date;

import java.util.Set;

 

public class NonBlockingServer {

    public static void main(String[] args) throws Exception {

        // Define a set of ports.

        int ports[] = { 8000, 8001, 8002, 8005 };

        // Creating a Selector.

        Selector selector = Selector.open();

        // Open some ServerSocketChannels and register in the selector.

        for (int i : ports) {

            ServerSocketChannel initServerSocketChannel = ServerSocketChannel

                    .open();

            // The Channel must be in non-blocking mode to be used with a

            // Selector.

            initServerSocketChannel.configureBlocking(false);

            ServerSocket initServerSocket = initServerSocketChannel.socket();

            initServerSocket.bind(new InetSocketAddress("127.0.0.1", i));

            // Registering Channels with the Selector.

            initServerSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("Server is listenning on port " + i);

        }

 

        // Once you have register one or more channels with a Selector you can

        // call one of the select()

        // methods. These methods return the channels that are "ready" for the

        // events you are interested in

        // (connect, accept, read or write). In other words, if you are

        // interested in channels that are ready for

        // reading, you will receive the channels that are ready for reading

        // from the select() methods.

        // Here are the select() methods:

        // int select()

        // int select(long timeout)

        // int selectNow()

        // select() blocks until at least one channel is ready for the events

        // you registered for.

        // select(long timeout) does the same as select() except it blocks for a

        // maximum of timeout milliseconds (the parameter).

        // selectNow() doesn't block at all. It returns immediately with

        // whatever channels are ready.

        // The int returned by the select() methods tells how many channels are

        // ready. That is, how many channels that became ready since last time

        // you called select(). If you call select() and it returns 1 because

        // one channel has become ready, and you call select() one more time,

        // and one more channel has become ready, it will return 1 again. If you

        // have done nothing with the first channel that was ready, you now have

        // 2 ready channels, but only one channel had become ready between each

        // select() call.

        while (selector.select() > 0) {

            Set<SelectionKey> selectedKeys = selector.selectedKeys();

            for (SelectionKey key : selectedKeys) {

                if (key.isAcceptable()) {

                    // Accessing the channel from the SelectionKey.

                    ServerSocketChannel server = (ServerSocketChannel) key

                            .channel();

                    SocketChannel client = server.accept();

                    System.out.println("Accepted");

                    client.configureBlocking(false);

                    ByteBuffer outBuf = ByteBuffer.allocate(1024);

                    outBuf.put(("Current Time is " + new Date()).getBytes());

                    outBuf.flip();

                    client.write(outBuf);

                    client.close();

                }

            }

            selectedKeys.clear();

        }

    }

}

 

Client.java

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.CharBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.nio.charset.Charset;

import java.nio.charset.CharsetDecoder;

import java.util.Set;

 

 

public class Client {

 

    public static void main(String[] args) throws Exception {

        Selector selector = Selector.open();

        SocketChannel initClientSocketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8001));

        // You must set the Channel to non-blocking.

        initClientSocketChannel.configureBlocking(false);

        initClientSocketChannel.register(selector, SelectionKey.OP_READ);

        

        while(selector.select()>0){

            Set<SelectionKey> selectedKeys = selector.selectedKeys();

            for(SelectionKey key : selectedKeys) {

                if(key.isReadable()) {

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

                    

                    ByteBuffer inBuf = ByteBuffer.allocate(1024);

                    clientSocketChannel.read(inBuf);

                    inBuf.flip();

                    Charset charset  =  Charset.forName"utf-8" );

                    CharsetDecoder decoder  =  charset.newDecoder();

                    CharBuffer charBuffer  =  decoder.decode(inBuf);

                    System.out.println(charBuffer);

                }

            }

        }

    }

 

}

事实上,客观地说,NIO的性能跟传统线程池性能的比较孰优孰劣有待考证,笔者能力有限,暂时没有写出可以供测试的模型出来,如果读者有兴趣可以参考下图写一个服务器程序出来。

图 4 NIO服务器模型

 

 

参考资料:

 

 

转载地址:http://cjebi.baihongyu.com/

你可能感兴趣的文章
用find命令查找最近修改过的文件
查看>>
Android2.1消息应用(Messaging)源码学习笔记
查看>>
在android上运行native可执行程序
查看>>
Phone双模修改涉及文件列表
查看>>
android UI小知识点
查看>>
Android之TelephonyManager类的方法详解
查看>>
android raw读取超过1M文件的方法
查看>>
ubuntu下SVN服务器安装配置
查看>>
MPMoviePlayerViewController和MPMoviePlayerController的使用
查看>>
CocoaPods实践之制作篇
查看>>
[Mac]Mac 操作系统 常见技巧
查看>>
苹果Swift编程语言入门教程【中文版】
查看>>
捕鱼忍者(ninja fishing)之游戏指南+游戏攻略+游戏体验
查看>>
iphone开发基础之objective-c学习
查看>>
iphone开发之SDK研究(待续)
查看>>
计算机网络复习要点
查看>>
Variable property attributes or Modifiers in iOS
查看>>
NSNotificationCenter 用法总结
查看>>
C primer plus 基础总结(一)
查看>>
剑指offer算法题分析与整理(一)
查看>>