zoukankan      html  css  js  c++  java
  • 关于Java的IO通信

    一、BIO通信

    1、原理

    BIO即同步阻塞模式一请求一应答的通信模型,该模型最大的问题就是缺乏弹性伸缩能力。当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系。

    由于线程是JAVA虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,

    并最终导致进程宕机或者僵死,不能对外提供服务。

    BIO的服务端通信模型:

    (1)采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接;

    (2)当接收到客户端的连接请求后,会为每一个客户端请求创建新的线程进行请求的处理;

    (3)处理完成后通过输出流返回信息给客户端,响应完成后销毁线程;

    (4)典型的一请求一应答的通信模型;

    (5)缺点:缺乏弹性伸缩能力;

    一个线程处理一个Socket连接,因为Java Socket是通过InputStream和OutputStream来进行网络读写操作,而这俩个的读写都是阻塞模式,

    所以当某个Socket链路的读写操作没有完成时,排在后面的Socket连接是无法得到处理的,长时间的等待可能会导致超时,

    因此在同步阻塞模式下,通常会采用一个Socket链路独占一个线程的模型。

    如图所示:

    二、伪异步IO通信

    1、原理

    为了解决同步阻塞IO(BIO)所面临的一个链路需要一个线程处理的问题,后来有人对它的线程模型进行了优化,后端通过一个线程池来处理多个客户端的请求接入,

    形成客户端个数M:线程池最大线程数N的比例关系,其中M可以远远大于N,通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽。

    伪异步IO通信特性:

    (1)采用线程池和任务队列实现;

    (2)线程池负责连接;

    (3)M请求N应答;

    (4)线程池阻塞;

    当有新的客户端接入的时候,将客户端的Socket封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK的线程池维护一个消息队列和N个活跃线程对消息队列中的任务进行处理。

    由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。

    但是伪异步IO通信也有其缺陷,当有大量客户端请求的时候,随着并发访问量的增长,伪异步IO就会造成线程池阻塞。

    如图所示:

     三、NIO通信

    1、原理

    NIO是非阻塞IO(Non-block IO),也有人称之为New IO,因为它相对于之前的IO类库是新增的,所以被称为New IO,这是它的官方叫法。

    它是在 JDK 1.4 中引入的。NIO 弥补了原来同步阻塞I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。

    通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就可以利用底层优化,这是原来的 I/O 包所无法做到的。

    2、NIO之缓冲区Buffer

    首先介绍缓冲区(Buffer)的概念,Buffer 是一个对象, 它包含一些要写入或者要读出的数据。 在 NIO类库 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,我们将数据直接写入或者将数据直接读到 Stream 对象中。

    在 NIO 库中,所有数据都是用缓冲区进行处理的。在读取数据时,它是直接读到缓冲区中;在写入数据时,它也是写入到缓冲区中。任何时候访问 NIO 中的数据,我们都是通过缓冲区进行读写操作。

    缓冲区实质上是一个数组。通常它是一个字节数组(ByteBuffer),也可以使用其它种类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,及维护读写位置(limit)等信息。

    最常用的缓冲区是ByteBuffer,一个ByteBuffer提供了一组功能用于操作byte数组。除了ByteBuffer,还有其它的一些缓冲区,事实上,每一种Java基本类型(除了Boolean类型)都对应有一种缓冲区,

    如下所示:

    (1)ByteBuffer:字节缓冲区;

    (2)CharBuffer:字符缓冲区;

    (3)ShortBuffer:短整型缓冲区;

    (4)IntBuffer:整型缓冲区;

    (5)LongBuffer:长整型缓冲区;

    (6)FloatBuffer:浮点型缓冲区;

    (7)DoubleBuffer:双精度浮点型缓冲区;

    3、NIO之通道Channel

    Channel是一个通道,可以通过它读取和写入数据,它就像自来水管一样,网络数据通过Channel读取和写入。通道与流的不同之处在于通道是双向的。

    而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道可以用于读、写或者同时用于读写。

    4、NIO之多路复用器Selector

    它是JAVA NIO编程的基础,熟练的掌握Selector对于掌握NIO编程至关重要。多路复用器提供选择已经就绪的任务的能力。

    简单来讲,Selector会不断的轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,

    然后通过SelectionKey可以获取就绪Channel的集合进行后续的IO操作。

    一个多路复用器Selector可以同时轮询多个Channel,由于JDK使用了epoll()代替传统的select实现,所以它并没有最大连接句柄1024/2048的限制。

    这也就意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端,这的确是一个巨大的改进。

    四、AIO通信

    1、原理

    与NIO不同,AIO需要一个连接注册读写事件和回调方法,当进行读写操作时,只须直接调用API的read或write方法即可。

    这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;

    对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。

    AIO异步通道提供了两种方式获取操作结果:

    (1)通过java.util.concurrent.Future类来表示异步操作的结果;

    (2)在执行异步操作的时候传入一个java.nio.channels.CompletionHandler接口的实现类作为操作完成的回调。

    AIO的异步套接字通道是真正的异步非阻塞IO,对应于UNIX网络编程中的事件驱动IO(AIO),它不需要通过多路复用器(Selector)对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。

    AIO通信的特性:

    (1)连接注册读写事件和回调函数;

    (2)读写方法异步;

    (2)主动通知程序;

    五、总结

  • 相关阅读:
    [Java]关于throw,throws,try{}catch(){} 悟寰轩
    jmx使用jmxmp协议连接器的实现 悟寰轩
    ActionContextCleanUp作用 悟寰轩
    空心验证码(定制) 悟寰轩
    struts升级:FileUploadInterceptor在struts 2.3.14.2的jar中修改了方法acceptFile中的参数 悟寰轩
    网络里的“逆世界” 悟寰轩
    离开时自动提示设为首页
    文本与图像上传到数据库
    在Delphi用vbscript的正则表达式
    ASP中输入特殊字符
  • 原文地址:https://www.cnblogs.com/ZJOE80/p/13559719.html
Copyright © 2011-2022 走看看