《开实》P802
Java IO一般包含两个部分:1.java.io包中堵塞型IO;2.java.nio包中的非堵塞型IO,通常称为New IO。
IO是面向流的,而NIO是面向块的。原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。系统运行的瓶颈一般在于IO操作,一般打开某个IO通道需要大量的时间,同时端口中不一定就有足够的数据,这样read方法就一直等待读取此端口的内容,从而浪费大量的系统资源。有人也许会提出使用java的多线程技术啊!但是在当前进程中创建线程也是要花费一定的时间和系统资源的,因此不一定可取。NIO可以实现高速I/O而无需编写自定义的本机代码
其中,IO的阻塞操作:1.接收键盘数据时,只要执行到BufferedReader的readLine()方法,程序就会一直阻塞等待键盘输入。
2.网络编程中,服务器端使用ServerSocket的accept()方法,服务器一直处于等待状态,知道客户端请求连接。当要处理多个连接时,需要采用多线程,但由于每个线程都要有自己的栈空间,而且由于阻塞会导致大量线程进行上下文切换,会使程序的运行效率低下。
NIO通过Selector、Channel、Buffer实现非阻塞操作:
其实现主要用Reactor(反应器)设计模式。这个设计模式与Observer模式类似,只不过Observer模式只处理一个事件源,而Reactor模式可以处理多个。
Selector实现了用一个单独的线程来管理多个通道,它类似于一个观察者。在实现时,把需要处理的Channel的IO事件(如connect、read)注册给Selector。Selector内部原理:对所有注册的Channel进行轮询访问,一旦轮询到某个Channel有注册的事件发生,就通过传回Selection-key的方式通知开发者对该Channel进行操作。key封装一个特定的Channel和一个特定的Selector之间的关系。这种通过轮询的方式在处理多线程请求时不需要上下文切换,而多线程的实现方式需要切换,同时也需要进行弹栈和压栈操作。所以NIO效率较高。
一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。
Buffer
是一个对象, 它包含一些要写入或者刚读出的数据。 在 NIO 中加入 Buffer
对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream
对象中。
在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。
ByteBuffer
不是 NIO 中唯一的缓冲区类型。事实上,对于每一种基本 Java 类型都有一种缓冲区类型:
-
ByteBuffer
-
CharBuffer
-
ShortBuffer
-
IntBuffer
-
LongBuffer
-
FloatBuffer
-
DoubleBuffer
Channel是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象。通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream
或者 OutputStream
的子类), 而通道
可以用于读、写或者同时用于读写。
因为它们是双向的,所以通道可以比流更好地反映底层操作系统的真实情况。特别是在 UNIX 模型中,底层操作系统通道是双向的。
Channel
是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流。
正如前面提到的,所有数据都通过 Buffer
对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
http://blog.csdn.net/z69183787/article/details/23666555