亲宝软件园·资讯

展开

Java NIO概述

写代码的木公 人气:2
传统的输入输出流都是阻塞的输入输出。举个列子:当用传统的流进行数据输入时,如果流中没有数据,它会阻塞当前线程往下执行,等到从流中读到数据为止。另外传统的输入输出流每次处理的是一个字节或一个字符,通常效率不是很高。从JDK 1.4开始 Java提供了NIO功能,可以代替传统的输入输出功能,在效率上也有很大提升。 标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中(双向操作)。NIO可以使用非阻塞模式。 ## NIO概述 **NIO在处理文件时会将文件的一段区域直接映射到内存中,这样访问文件时就可以像访问内存一样,比传统的输入输出要快很多**。主要的实现类都在java.nio下面。 `Channe`l和`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来让我们操作这块数据区。 ```java 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的使用 ```java 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不能切换到非阻塞模式。而套接字通道都可以**。 ```java 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模型**。 ## 参考 - https://www.cnblogs.com/blackjoyful/p/11534985.html

加载全部内容

相关教程
猜你喜欢
用户评论