Loading

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模型

参考

公众号推荐

欢迎大家关注我的微信公众号「程序员自由之路」

posted @ 2020-03-17 11:00  程序员自由之路  阅读(820)  评论(0编辑  收藏  举报