1:IO 流

  • Byte(字节)是计算机操作数据的最小单位由 8 位 bit 组成 取值(-128-127)
  • Char(字符)是用户的可读写的最小单位,在 Java 里面由 16 位 bit 组成 取值(0-65535)

1-1:为何还要有字符流

在各种编码中,一个英文字符就是一个字节。如果只有英文就不会有问题,但是中文一般会占据多个字节。比如 Java 标准采用的 Unicode 编码中就占用两个字节,UTF-8 中占用三个字节,如果不知道字符编码,一次读取一个字节,可能就会将一个字符的多个字节分割开,导致乱码。

字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。

1-2:字节流和字符流区别

  • 字节流操作的基本单位为字节,字符流操作的基本单元为 Unicode 单元。
  • 字节流默认不使用缓冲区,字符流使用缓冲区
  • 字节流通常用处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取 Unicode 马原;字符流通常用于处理文本数据,它支持写入和读取 Unicode 码元。

2:BIO\NIO\AIO

2-1:BIO\NIO\AIO 定义

  • BIO:同步并阻塞 IO,数据的读取写入必须阻塞在一个线程内等待其完成.使用多线程解决多个客户端访问请求.
  • NIO:NIO 与原来的 I/O 有同样的作用和目的, 而 NIO 是支持同步非阻塞的.
  • AIO:异步非阻塞 IO

2-2:BIO 与 NIO 的区别

  1. BIO 是同步非阻塞的,而 NIO 支持同步阻塞和同步非阻塞两种模式
  2. BIO 面向流,而 NIO 面向缓冲区(Buffer oriented).BIO 中是将数据直接写入或者读取到 Stream 对象中,而 NIO 所有数据都是通过缓冲区处理.在读取数据时,直接冲缓冲区读取,写入数据时,写入到缓冲区.
  3. NIO 通过 Channel(通道)进行读写,通道是双向的,可以读也可以写,而流的读写是单向的.无论读写,通道只能和 Buffer 交互。也因为 Buffer,通道可以异步地读写。
  4. NIO 有选择器,BIO 没有.

3:IO 模型

3-1:IO 多路复用

在 IO 多路复用模型中,会有一个线程不断去轮询多个 socket 的状态,只有 socket 真正有读写事件时,才真正调用实际的 IO 读写操作.
因为使用一个线程来管理,不需要创建新的进程或者线程,并且在真正有 IO 事件时才会占用资源,所以大大减少了资源占用.

3-2:IO 多路复用实现方式

  • select
  • poll
  • epoll

3-2-1:三种实现方式区别

select poll epoll
数据结构 bitmap 数组 红黑树
最大连接数 1024 无上限 无上限
fd 拷贝 每次调用 select 拷贝 每次调用 poll 拷贝 fd 首次调用 epoll_ct 拷贝,每次调用 epoll_wait 不拷贝
工作效率 轮询:O(n) 轮询:O(n) 回调:O(1)

3-2-2:三种常用的实现方式优缺点

  1. select
    • 单个进程所打开的 FD 是有限制的,通过 FD_SETSIZE 设置,默认 102
    • 每次调用 select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大
    • 对 socket 扫描时是线性扫描,采用轮询的方法,效率较低(高并发时)
  2. poll
    • 每次调用 poll,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大
    • 对 socket 扫描时是线性扫描,采用轮询的方法,效率较低(高并发时)
  3. epoll
    • epoll 只能工作在 linux 下