Java NIO基础知识整理(二)

非阻塞I/O

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

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

Java <wbr>NIO基础知识整理(二)

图 2 网络 IO 通讯流程

 

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

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

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

Java <wbr>NIO基础知识整理(二)

图 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的性能跟传统线程池性能的比较孰优孰劣有待考证,笔者能力有限,暂时没有写出可以供测试的模型出来,如果读者有兴趣可以参考下图写一个服务器程序出来。

Java <wbr>NIO基础知识整理(二)

图 4 NIO服务器模型

 

 

参考资料:

http://g.oswego.edu/dl/cpjslides/nio.pdf 

http://tutorials.jenkov.com/java-nio/index.html 

http://www.javaperformancetuning.com/tips/nio.shtml

转载于:https://my.oschina.net/u/174865/blog/37053

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值