欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java NIO概述

程序员文章站 2022-03-25 17:50:38
传统的输入输出流都是阻塞的输入输出。举个列子:当用传统的流进行数据输入时,如果流中没有数据,它会阻塞当前线程往下执行,等到从流中读到数据为止。另外传统的输入输出流每次处理的是一个字节或一个字符,通常效率不是很高。从JDK 1.4开始 Java提供了NIO功能,可以代替传统的输入输出功能,在效率上也有 ......

传统的输入输出流都是阻塞的输入输出。举个列子:当用传统的流进行数据输入时,如果流中没有数据,它会阻塞当前线程往下执行,等到从流中读到数据为止。另外传统的输入输出流每次处理的是一个字节或一个字符,通常效率不是很高。从jdk 1.4开始 java提供了nio功能,可以代替传统的输入输出功能,在效率上也有很大提升。

标准的io基于字节流和字符流进行操作的,而nio是基于通道(channel)和缓冲区(buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中(双向操作)。nio可以使用非阻塞模式。

nio概述

nio在处理文件时会将文件的一段区域直接映射到内存中,这样访问文件时就可以像访问内存一样,比传统的输入输出要快很多。主要的实现类都在java.nio下面。

channel和buffer是nio中两个核心的概念。channel的概念和传统的inputstram和outputstream对标,最大的区别是channel提供了一个map()方法将文件的块数据映射到内存中。可以面向一大块数据进行处理。buffer可以理解成缓冲,其本质是一个数组。从channel中读出来的数据要先存在buffer中,要写到channel中的数据也要先放到buffer中。

另外,nio还提供了将unicode字符串映射成字节序列的charset类,以及支持非阻塞输入输出的selector类。

channels and buffers

标准的io基于字节流和字符流进行操作的,而nio是基于通道(channel)和缓冲区(buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。channel、buffer和selectors是nio的核心组件。

channel常用的实现类:

  • filechannel:文件
  • datagramchannel:udp数据报
  • socketchannel:tcp客户端
  • serversocketchannel:tcp服务端

buffer常见实现类:

  • bytebuffer
  • charbuffer
  • doublebuffer
  • floatbuffer
  • intbuffer
  • longbuffer
  • shortbuffer

buffer的使用

buffer的本质就是一个缓冲区,但是buffer提供了丰富的api来让我们操作这块数据区。

system.out.println("capacity:"+buffer.capacity());
system.out.println("limit:"+buffer.limit());
system.out.println("length:"+buffer.length());
system.out.println("position:"+buffer.position());

buffer.append("a");
buffer.append('b');
buffer.put('c');
system.out.println("---------------------------");

system.out.println("capacity:"+buffer.capacity());
system.out.println("limit:" + buffer.limit());
system.out.println("length:" + buffer.length());
system.out.println("position:" + buffer.position());

//flip方法会将limit的位置移动到当前posiion位置,这样buffer中没
//赋值的空间将都不能被访问。通常flip方法是为读取数据做准备的,可以
//防止读到null数据,读取完毕之后调用clear方法
buffer.flip();
system.out.println("---------------------------");

system.out.println("capacity:"+buffer.capacity());
system.out.println("limit:" + buffer.limit());
system.out.println("length:" + buffer.length());
system.out.println("position:" + buffer.position());

channel的使用

fileinputstream fis = new fileinputstream("file.txt");
filechannel channel = fis.getchannel();

bytebuffer buffer = bytebuffer.allocate(1024);
int hasread = 0;
while ((hasread=channel.read(buffer))>0){
byte[] buff = new byte[1024];
buffer.flip();
buffer.get(buff, 0, hasread);
system.out.println(new string(buff,0,hasread));
buffer.clear();
}

selector

selector(选择器)是java nio中能够检测一到多个nio通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。与selector一起使用时,channel必须处于非阻塞模式下。这意味着不能将filechannel与selector一起使用,因为filechannel不能切换到非阻塞模式。而套接字通道都可以

selector selector = selector.open();
channel.configureblocking(false);
//注册到selector上
selectionkey key = channel.register(selector, selectionkey.op_read);
while(true) {
    int readychannels = selector.select();
    if(readychannels == 0) 
        continue;
    set selectedkeys = selector.selectedkeys();
    iterator keyiterator = selectedkeys.iterator();
    while(keyiterator.hasnext()) {
        selectionkey key = keyiterator.next();
        if(key.isacceptable()) {
        // a connection was accepted by a serversocketchannel.
        } else if (key.isconnectable()) {
        // a connection was established with a remote server.
        } else if (key.isreadable()) {
        // a channel is ready for reading
        } else if (key.iswritable()) {
        // a channel is ready for writing
         }
      keyiterator.remove();
   }
}

charset

用于对字符串编解码

jdk7的nio2

jdk 1.7版本对nio进行优化改进。path、paths和files这些类、filevisiter、watchservice asynchronousfilechannel这些类进行文件内容的异步读写。asynchronoussocketchannel这些类进行服务器io异步读写。

bio、nio和aio的区别

  • bio (blocking i/o):同步阻塞i/o模式,数据的读取写入必须阻塞在一个线程内等待其完成。这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,bio的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。(特点就是线程必须等待数据读取或者写入完成才能继续干其他事情。

  • nio (new i/o):同时支持阻塞与非阻塞模式(文件channel只支持阻塞模式,socket的channel支持阻塞和非阻塞模式),但这里我们以其同步非阻塞i/o模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,nio的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。(特点就是线程不必等待io读写完成,在io进行过程中线程可以不停地轮询io的状态,一旦发现io状态变化,就可以做出相应处理

  • aio ( asynchronous i/o):异步非阻塞i/o模型。异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有io操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。aio中虽然不需要线程来轮询,但是需要线程来等待通知。

    另外,aio的异步特性并不是java实现的,而是使用了系统底层api的支持,在unix系统下,采用了epoll io模型,而windows便是使用了iocp模型

参考