理解概念

什么是Java IO

  • Java IO是Java Input And Out的简称,是Java的输入输出操作
  • I:InputStream 输入流
  • O:OutputStream 输出流
  • 流:词义来自水流,意指连续不断的数据流,本质是无结构化的连续的字节数据或字符数据,即忽略文件的结构,按照字节或字符去读取或写入数据。
  • Java IO 是Java应用程序和操作系统数据交互的过程,本质是Java进程中某线程和操作系统内核的数据交互,表现形式是JDK的IO操作API或类库
  • 学习Java IO,就是学习如何使用Java调用操作系统的IO,本质是学习JDK类库的API
  • Java IO API分为BIO和NIO,即阻塞IO和非阻塞IO

    阻塞和非阻塞

  • 阻塞blocking:线程读取或写入流时,一直停止,直到数据流处理完毕,线程就绪后再继续执行

  • 非阻塞non-blocking:线程被调用后直接返回一个状态值,读取或写入流操作在后台进行,线程可以继续执行

    同步和异步

  • 同步synchronous:线程发起IO请求后,需等待处理完成后才能继续执行,同步可以保证执行顺便,避免数据脏读

  • 异步asynchronous:线程发起IO请求后,仍继续执行,当IO操作完成后操心系统内核通知线程组(事件机制)或调用线程注册的回调函数(回调机制),通知线程,异步可以提高效率,提高并发

    串行,并行和并发

  • 串行,从开始到结束依次执行,一连串的执行

  • 并行,多个一起同步执行
  • 并发,宏观上是并行,本质上是串行,CPU就是并发操作,宏观并行,微观通过时间片轮转串行执行

总结:

  1. 同步和异步重在发送请求和接收响应的过程
  2. 阻塞和非阻塞重在流处理过程
  3. 串行和并行重在执行过程中是一个线程从头到尾依次执行,还是多个线程齐头并进一起执行
  4. 并发是宏观串行,微观并行,利用时间片轮转达到逻辑上的并行

    BIO、NIO和AIO

  • BIO:Blocing-IO 同步阻塞IO模型,数据的读写必须在同一线程内,等待完成
  • NIO:Non-Blocking-IO 同步非阻塞IO模型,多线程读写操作,一个线程不断轮询每个操作的状态,从而进行下一步操作
  • AIO:Synchronous-Non-Blocking-IO 异步非阻塞IO模型,多线程读写操作,每个操作完后后主动事件通知,无需一个线程去轮询

举个例子:烧开水,假设有一排壶在烧水,则:

  • BIO:就是有一个人一个炉子,先烧烤第一壶水,然后去烧第二壶水,直到全部烧开为止
  • NIO:就是有一个人多个炉子,多个壶同时烧水,哪个开了处理哪个,直到全部烧开为止
  • AIO:就是有一根人多个自动跳闸的炉子,多个壶同时烧水,水烧开了壶自动跳闸

特别说明:技术没有银弹,没有什么最优解,AIO最快,但是需要你购买带自动开关的炉子,BIO最慢,但是只需要一个炉子,NIO居中,但是需要你不停的照看,你需要做的就是,合适的场景选择合适的技术

JDK1.0 引入BIO,JDK1.4引入NIO,JDK1.7引入AIO

  • BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
  • NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
  • AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

    IO和NIO的区别

    Java IO - 图1

BIO的四种类型

传统的 IO 大致可以分为4种类型:

  • InputStream、OutputStream 基于字节操作的 IO
  • Writer、Reader 基于字符操作的 IO
  • File 基于磁盘操作的 IO
  • Socket 基于网络操作的 IO

    NIO的3个核心概念

    NIO重点是把Channel(通道),Buffer(缓冲区),Selector(选择器)三个类之间的关系弄清楚。
    1.缓冲区Buffer
    Buffer是一个对象。它包含一些要写入或者读出的数据。在面向流的I/O中,可以将数据写入或者将数据直接读到Stream对象中。
    在NIO中,所有的数据都是用缓冲区处理。这也就本文上面谈到的IO是面向流的,NIO是面向缓冲区的。
    缓冲区实质是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息。
    最常用的缓冲区是ByteBuffer,一个ByteBuffer提供了一组功能于操作byte数组。除了ByteBuffer,还有其他的一些缓冲区,事实上,每一种Java基本类型(除了Boolean)都对应一种缓冲区,具体如下:

  • ByteBuffer:字节缓冲区

  • CharBuffer:字符缓冲区
  • ShortBuffer:短整型缓冲区
  • IntBuffer:整型缓冲区
  • LongBuffer:长整型缓冲区
  • FloatBuffer:浮点型缓冲区
  • DoubleBuffer:双精度浮点型缓冲区

2.通道Channel
Channel是一个通道,可以通过它读取和写入数据,他就像自来水管一样,网络数据通过Channel读取和写入。
通道和流不同之处在于通道是双向的,流只是在一个方向移动,而且通道可以用于读,写或者同时用于读写。
因为Channel是全双工的,所以它比流更好地映射底层操作系统的API,特别是在UNIX网络编程中,底层操作系统的通道都是全双工的,同时支持读和写。
Channel有四种实现:

  • FileChannel:是从文件中读取数据。
  • DatagramChannel:从UDP网络中读取或者写入数据。
  • SocketChannel:从TCP网络中读取或者写入数据。
  • ServerSocketChannel:允许你监听来自TCP的连接,就像服务器一样。每一个连接都会有一个SocketChannel产生。

3.多路复用器Selector
Selector选择器可以监听多个Channel通道感兴趣的事情(read、write、accept(服务端接收)、connect,实现一个线程管理多个Channel,节省线程切换上下文的资源消耗。Selector只能管理非阻塞的通道,FileChannel是阻塞的,无法管理。
关键对象

  • Selector:选择器对象,通道注册、通道监听对象和Selector相关。
  • SelectorKey:通道监听关键字,通过它来监听通道状态。

监听注册
监听注册在Selector socketChannel.register(selector, SelectionKey.OP_READ);

监听的事件有

  • OP_ACCEPT: 接收就绪,serviceSocketChannel使用的
  • OP_READ: 读取就绪,socketChannel使用
  • OP_WRITE: 写入就绪,socketChannel使用
  • OP_CONNECT: 连接就绪,socketChannel使用