BIO与NIO、AIO的区别(这个容易理解)

 IO的方式一般分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。编程

1、BIO后端

     在 JDK1.4出来以前,咱们创建网络链接的时候采用BIO模式,须要先在服务端启动一个ServerSocket,而后在客户端启动Socket来对服务 端进行通讯,默认状况下服务端须要对每一个请求创建一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,若是没有则会一直等待或者遭到拒绝 请求,若是有的话,客户端会线程会等待请求结束后才继续执行。缓存

2、NIO服务器

    NIO 自己是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题: 在使用同步I/O的网络应用中,若是要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通信,就必须使用多线程来处理。也就是说,将每个客 户端请求分配给一个线程来单独处理。这样作虽然能够达到咱们的要求,但同时又会带来另一个问题。因为每建立一个线程,就要为这个线程分配必定的内存空间 (也叫工做存储器),并且操做系统自己也对线程的总数有必定的限制。若是客户端的请求过多,服务端程序可能会由于不堪重负而拒绝客户端的请求,甚至服务器 可能会所以而瘫痪。网络

    NIO基于Reactor,当socket有流可读或可写入socket时,操做系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操做系 统。  也就是说,这个时候,已经不是一个链接就要对应一个处理线程了,而是有效的请求,对应一个线程,当链接没有数据时,是没有工做线程来处理的。多线程

   BIO与NIO一个比较重要的不一样,是咱们使用BIO的时候每每会引入多线程,每一个链接一个单独的线程;而NIO则是使用单线程或者只使用少许的多线程,每一个链接共用一个线程。架构

      NIO的最重要的地方是当一个链接建立后,不须要对应一个线程,这个链接会被注册到多路复用器上面,因此全部的链接只须要一个线程就能够搞定,当这个线程 中的多路复用器进行轮询的时候,发现链接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。并发

   HTTP/1.1出现后,有了Http长链接,这样除了超时和指明特定关闭的http header外,这个连接是一直打开的状态的,这样在NIO处理中能够进一步的进化,在后端资源中能够实现资源池或者队列,当请求来的话,开启的线程把请 求和请求数据传送给后端资源池或者队列里面就返回,而且在全局的地方保持住这个现场(哪一个链接的哪一个请求等),这样前面的线程仍是能够去接受其余的请求, 然后端的应用的处理只须要执行队列里面的就能够了,这样请求处理和后端应用是异步的.当后端处理完,到全局地方获得现场,产生响应,这个就实现了异步处 理。app

NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操做,而NIO基于Channel和Buffer(缓冲区)进行操做,数据老是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(好比:链接打开,数据到达)。所以,单个线程能够监听多个数据通道。

NIO和传统IO(一下简称IO)之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取全部字节,它们没有被缓存在任何地方。此外,它不能先后移动流中的数据。若是须要先后移动从流中读取的数据,须要先将它缓存到一个缓冲区。NIO的缓冲导向方法略有不一样。数据读取到一个它稍后处理的缓冲区,须要时可在缓冲区中先后移动。这就增长了处理过程当中的灵活性。可是,还须要检查是否该缓冲区中包含全部您须要处理的数据。并且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里还没有处理的数据。

IO的各类流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据彻底写入。该线程在此期间不能再干任何事情了。 NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,可是它仅能获得目前可用的数据,若是目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,因此直至数据变的能够读取以前,该线程能够继续作其余的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不须要等待它彻底写入,这个线程同时能够去作别的事情。 线程一般将非阻塞IO的空闲时间用于在其它通道上执行IO操做,因此一个单独的线程如今能够管理多个输入和输出通道

 

Channel

首先说一下Channel,国内大多翻译成“通道”。Channel和IO中的Stream(流)是差很少一个等级的。只不过Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既能够用来进行读操做,又能够用来进行写操做。
NIO中的Channel的主要实现有:

    FileChannel
    DatagramChannel
    SocketChannel
    ServerSocketChannel

这里看名字就能够猜出个因此然来:分别能够对应文件IO、UDP和TCP(Server和Client)。下面演示的案例基本上就是围绕这4个类型的Channel进行陈述的。
Buffer

NIO中的关键Buffer实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。固然NIO中还有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等这里先不进行陈述。
Selector

Selector运行单线程处理多个Channel,若是你的应用打开了多个通道,但每一个链接的流量都很低,使用Selector就会很方便。例如在一个聊天服务器中。要使用Selector, 得向Selector注册Channel,而后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就能够处理这些事件,事件的例子有如新的链接进来、数据接收等。

 

 

 

 

 

NIO是一个请求一个线程。异步

先来个例子理解一下概念,以银行取款为例: 

  • 异步 : 委托一小弟拿银行卡到银行取钱,而后给你(使用异步IO时,Java将IO读写委托给OS处理,须要将数据缓冲区地址和大小传给OS(银行卡和密码),OS须要支持异步IO操做API);
  • 非 阻塞 : 柜台取款,取个号,而后坐在椅子上作其它事,等号广播会通知你办理,没到号你就不能去,你能够不断问大堂经理排到了没有,大堂经理若是说还没到你就不能去 (使用非阻塞IO时,若是不能读写Java调用会立刻返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)

Java BIO : 同步并阻塞,服务器实现模式为一个链接一个线程,即客户端有链接请求时服务器端就须要启动一个线程进行处理,若是这个链接不作任何事情会形成没必要要的线程开销,固然能够经过线程池机制改善。

  • Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,

BIO方式适用于链接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4之前的惟一选择,但程序直观简单易理解。

  • AIO方式使用于链接数目多且链接比较长(重操做)的架构,好比相册服务器,充分调用OS参与并发操做,编程比较复杂,JDK7开始支持。

在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操做。

 通常来讲I/O模型能够分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO

在此种方式下,用户进程在发起一个IO操做之后,必须等待IO操做的完成,只有当真正完成了IO操做之后,用户进程才能运行。JAVA传统的IO模型属于此种方式!

在此种方式下,用户进程发起一个IO操做之后边可返回作其它事情,可是用户进程须要时不时的询问IO操做是否就绪,这就要求用户进程不停的去询问,从而引入没必要要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。

此 种方式下是指应用发起一个IO操做之后,不等待内核IO操做的完成,等内核完成IO操做之后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须 等待或者主动的去询问IO是否完成,那么为何说是阻塞的呢?由于此时是经过select系统调用来完成的,而select函数自己的实现方式是阻塞的, 而采用select函数有个好处就是它能够同时监听多个文件句柄,从而提升系统的并发性!

在 此种模式下,用户进程只须要发起一个IO操做而后当即返回,等IO操做真正的完成之后,应用程序会获得IO操做完成的通知,此时用户进程只须要对数据进行 处理就行了,不须要进行实际的IO读写操做,由于真正的IO读取或者写入操做已经由内核完成了。目前Java中尚未支持此种IO模型

 

 

 

 

 

【当你用心写完每一篇博客以后,你会发现它比你用代码实现功能更有成就感!】
相关文章
相关标签/搜索