java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java处理IO数据

Java同步非阻塞模式NIO处理IO数据

作者:goyeer

这篇文章主要介绍了Java同步非阻塞模式NIO处理IO数据,服务器实现模式为一个请求一个线程,即客户端发送的链接请求都会注册到选择器上,选择器轮询到连接有IO请求时才启动一个线程进行处理,需要的朋友可以参考下

一、概述

NIO(Non-Blocking IO)是同步非阻塞方式来处理IO数据。服务器实现模式为一个请求一个线程,即客户端发送的链接请求都会注册到选择器上,选择器轮询到连接有IO请求时才启动一个线程进行处理。

二、常用概念

同步和异步强调的是消息通信机制 (synchronous communication/ asynchronous communication)。所谓同步,就是在发出一个"调用"时,在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由“调用者”主动等待这个“调用”的结果。而异步则是相反,"调用"在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在"调用"发出后,"被调用者"通过状态、通知来通知调用者,或通过回调函数处理这个调用

阻塞和非阻塞 强调的是程序在等待调用结果(消息,返回值)时的状态. 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。 对于同步调用来说,很多时候当前线程还是激活的状态,只是从逻辑上当前函数没有返回而已,即同步等待时什么都不干,白白占用着资源。

三、NIO的实现原理

Java的NIO主要由三个核心部分组成:Channel(通道)、Buffer(缓冲区)、Selector。

所有的IO在NIO中都从一个Channel开始,数据可以从Channel读到Buffer中,也可以从Buffer写到Channel中。Channel有好几种类型,其中比较常用的有FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel等,这些通道涵盖了UDP和TCP网络IO以及文件IO。

Buffer本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。Java NIO里关键的Buffer实现有CharBuffer、ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。这些Buffer覆盖了你能通过IO发送的基本数据类型,即byte、short、int、long、float、double、char。

Buffer对象包含三个重要的属性,分别是capacity、position、limit,其中position和limit的含义取决于Buffer处在读模式还是写模式。但不管Buffer处在什么模式,capacity的含义总是一样的。

capacity:作为一个内存块,Buffer有个固定的最大值,就是capacity。Buffer只能写capacity个数据,一旦Buffer满了,需要将其清空才能继续写数据往里写数据。

position:当写数据到Buffer中时,position表示当前的位置。初始的position值为0。当一个数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity–1。当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0。当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。

limit:在写模式下,Buffer的limit表示最多能往Buffer里写多少数据,此时limit等于capacity。当切换Buffer到读模式时, limit表示你最多能读到多少数据,此时limit会被设置成写模式下的position值。

Selector允许单线程处理多个 Channel,如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件例如有新连接进来,数据接收等。

四、NIO代码实现

客户端实现

步骤

代码演示

public static void main(String[] args) throws IOException {
      //创建通道
      SocketChannel channel=SocketChannel.open(new InetSocketAddress("127.0.0.1",6001));
      //切换异步非阻塞模式
      channel.configureBlocking(false);
      //设置缓冲去大小
      ByteBuffer buffer=ByteBuffer.allocate(1024);
      System.out.println("输入传输值:");
      //获取键盘输入的值
      Scanner scanner = new Scanner(System.in);
      while (scanner.hasNext()){
          String input=scanner.next();
          //把获取的值写入缓冲区中
          buffer.put(input.getBytes());
          buffer.flip();
          //把缓冲区中的值写入通道中
          channel.write(buffer);
          buffer.clear();
       }
       channel.close();
 }

服务端实现

步骤

代码演示

public static void main(String[] args) throws IOException {
    //创建通道
    ServerSocketChannel channel=ServerSocketChannel.open();
    //切换到异步非阻塞模式
    channel.configureBlocking(false);
    //绑定链接
    channel.bind(new InetSocketAddress(6001));
        //获取选择器
        Selector open = Selector.open();
        //将通道注册到选择器,并指定监听接受事件
        channel.register(open, SelectionKey.OP_ACCEPT);
        //轮训式获取选择已经准备就绪的事件
        while(open.select() > 0) {
            //获取当前选择器所有注册的监听事件
            Iterator<SelectionKey> it = open.selectedKeys().iterator();
            while(it.hasNext()) {
                //获取准备就绪的事件
                SelectionKey sk = it.next();
                //判断是什么事件准备就绪
                if(sk.isAcceptable()) {
                    //接受就绪,获取客户端连接
                    SocketChannel sc = channel.accept();
                    //设置非阻塞异步模式
                    sc.configureBlocking(false);
                    //将通道注册到服务器上
                    sc.register(open, SelectionKey.OP_READ);
                } else if(sk.isReadable()) {
                    //获取当前选择器就绪的通道
                    SocketChannel s =  (SocketChannel) sk.channel();
                    ByteBuffer bb = ByteBuffer.allocate(1024);
                    int len = 0;
                    while((len = s.read(bb)) > 0) {
                        bb.flip();
                        System.out.println(new String(bb.array(),0,len));
                        bb.clear();
                    }
               }
          }
         it.remove();
     }
}

五、同步非阻塞NIO总结

同步非阻塞的特点:应用程序的线程需要不断的进行IO系统调用,轮询数据是否已经准备好,如果没有准备好,就继续轮询,直到完成IO系统调用为止。

同步非阻塞IO的特点:每次发起的IO系统调用,在内核等待数据过程中可以立即返回。用户线程不会被阻塞,实时性较好。

同步非阻塞IO的缺点: 不断地轮询内核,这将占用大量的CPU时间,效率低下。

总体来说,在高并发应用场景下,同步非阻塞IO也是不可用的。一般Web服务器不适用这种IO模型。这种IO模型一般很少直接使用,而是在其他IO模型中使用非阻塞IO这一特性。

以上就是Java同步非阻塞模式NIO处理IO数据的详细内容,更多关于Java处理IO数据的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文