最传统的一种IO模式,即在读写的过程中发生阻塞,当用户发起IO请求后,内核会去查看数据是否就绪,如果没有就绪会等待线程就绪,而用户的请求线程就会进入阻塞状态,用户线程交出cpu控制权,当数据准备就绪,内核将会数据拷贝到用户线程,用户线程才会接触block状态。典型的IO模式例子就是data:socket.read()。
非阻塞IO则是在read的时候不需要等待,如果得到结果是false,则知道数据没有准备好,这时候则会继续发送read操作,一旦数据在buffe缓存里准备好了,这时候则可以获取到数据,然后返回。所以他的cpu控制权是不是释放的,会一直使用。
多路复用IO模式,是目前使用比较多的模式,上面说的非阻塞IO实际上就是多路复用IO,他的工作原理是:有一个线程不断的去轮询多个socket的状态,当socket真的发生了读写事件的时候,才会真正的调用read。这里是一个线程管理多个socket,而不是普通的NIO通过select()方法来read数据,当只有socket真的发生读写的时候,才会占用cpu资源去进行读操作。
另外为何多路复用比NIO效率更高,因为NIO是在不断的发送read查socket数据的,而多路复用,是把有数据的socket放在一个缓存区,轮询看哪个socket有数据,再去read()。不过多路复用IO是通过轮询的方式检测是否有事件大到达,并对事件逐一响应,当某个事件很大的时候,会影响后面事件的处理效率和事件轮播。
信号驱动IO,当用户发起一个IO请求的时候,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时候会发送信号给用户线程,这时候用户线程会调用IO操作read读数据。
异步IO模式可以说是最理想的IO,当用户线程发起读操作的时候就可以去做其他事,完全不会发生阻塞,而内核的角度,java7提供的AsynchronousIO read之后,会立刻返回数据,说明数据已经返回,不会对用户线程block,然后内核数据准备完毕吧数据拷贝到用户线程,然后给用户线程发送一个信号,让他来读取数据,不需要用户线程发起IO操作,意思就是当数据准备完毕,用户线程读取数据之前,IO操作已经结束了。也就是说在异步中,IO的两个操作都不会阻塞,两个阶段都是由内核完成的。
JAVA NIO
Java的三大核心部分:Channel(通道),Buffer(缓存区),Selector。传统的IO基于字节和字符进行操作,而NIO则是通过Channel通道和buffer缓存来进行操作,数据总是通过通道发送到缓存,在从缓存读取到通道。Selector选择区用于监听多个通道事件(比如连接打开,数据到达),因此单个线程可以监听多个数据通道。(所以NIO是面向缓冲区的,IO是面向流的)
NIO的缓冲区是什么呢?java io面向的是流,意味着每次从流中读取一个或者多个字节,直到全部读取完,不会存在任何地方,此外他不能前后移动流中的数据,如果需要移动,则吧他们先存到缓冲区。NIO的缓冲和java 的io不同,数据先读取到缓冲区,可以在里面前后移动,这就增加了数据处理的灵活性,但是还需要检测缓冲区是否有您处理的数据,已经保证数据量过大的时候,不会覆盖缓冲区里的未处理数据。
NIO的非阻塞,io的各种流都是阻塞的,意味着当线程调用read后者write的时候,会进入阻塞状态,不能做任何其他事,直到数据完全查询或者写入。NIO的非阻塞模式使一个线程从通道发送请求数据,但他仅能得到目前缓存区已经有的数据,如果没有数据,则什么都没有获取到,也就不会阻塞影响线程做其他事。非阻塞写也是如此,当线程请求通道写入的时候,不需要等待写入完毕,则可以去做其他事。这就是为什么一个线程可以管理多个输入输出通道channel的原因。
Channel和io中的stream流差不多一个等级的。只不过stream是单向的,但是channel是双向的,可以读也可以写。主要实现有FileChannel、DatagramChannel、socketChannel、ServerSocketChannel。
Buffer缓冲区实际就是一个容器,一个连续的数组,channel提供从文件网络读取数据的渠道,但是读取和写入都必须经过buffer。 Buffer是一个抽象类,常用的有byteBuffer,intBffer等。他的流程就是数据读的时候先进入buffer然后到通道里,写数据的时候也是先从通道到buffer然后到server。
Selector是NIO的一个核心类,selector可以检测多个注册通道是否有事件发生,以便获取事件然后针对每个事件进行响应处理。这样一来只需要单个线程就能处理多个通道,当真的监听到读写时间,才会调用函数,减少了系统的开销,不必为每个连接都创建一个线程,不用维护线程,避免上下文切换。