1:同步 异步 阻塞 非阻塞 有什么区别?

  1. 阻塞和非阻塞阻塞(针对调用方):
  2. 阻塞:
  3. 发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
  4. 非阻塞:
  5. 发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
  6. 同步与异步同步(针对于被调用方):
  7. 同步:
  8. 发起一个调用后,被调用者未处理完请求之前,调用不返回。
  9. 异步:
  10. 发起一个调用后,立刻得到被调用者的回应表示已接收到请求,
  11. 但是被调用者并没有返回结果,此时我们可以处理其他的请求,
  12. 被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
  13. 特别注意 IO模型中
  14. 调用方是服务端 被调用方是客户端
  15. 调用方是服务端 被调用方是客户端
  16. 调用方是服务端 被调用方是客户端
  17. 因为是 服务器 使用资源一直等待(请求) 客户端的连接请求
  18. 有点绕口 请看下面案例
  19. 人(调用方)(服务端) 等待 水烧开(被调用方)(客户端)
  20. 人烧水
  21. 1:同步阻塞
  22. 人在那里 傻等着(浪费服务端的资源) 水烧开(客户端 万一只是假装说说而已)。
  23. 2:同步非阻塞
  24. 人先去忙的自己的事情,时不时回来看看(服务端 selector 轮训监听)水有没有烧开
  25. 3 异步非阻塞
  26. 人先去忙的自己的事情,新买的烧水开了 (会发出声音的水壶) 会通知人

2:BIO,NIO,AIO 有什么区别?

  1. BIO (Blocking I/O):
  2. 同步阻塞 I/O 模式,数据的读取写入必须阻塞在一个线程内等待其完成。
  3. 适用于在活动连接数不是特别高(小于单机 1000)的情况下:
  4. 可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。
  5. 线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。
  6. 但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。
  7. NIO (Non-blocking/New I/O):
  8. NIO 是一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入了 NIO 框架,
  9. 对应 java.nio 包,提供了 Channel , SelectorBuffer 等抽象。
  10. NIO 中的 N 可以理解为 Non-blocking,不单纯是 New
  11. 它支持面向缓冲的,基于通道的 I/O 操作方法。
  12. NIO 提供了与传统 BIO 模型中的 Socket ServerSocket 相对应的 SocketChannel ServerSocketChannel
  13. 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。
  14. 阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;
  15. 非阻塞模式正好与之相反。
  16. 对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;
  17. 对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
  18. AIO (Asynchronous I/O):
  19. AIO 是异步非阻塞的 IO 模型 ,在 Java 7 中引入了 NIO 的改进版 NIO 2AIO
  20. 异步 IO 是基于事件和回调机制实现:
  21. 应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
  22. 虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO IO 行为还是同步的。
  23. 对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,
  24. 接着就由这个线程自行进行 IO 操作,IO 操作本身是同步的。

image.png

3:BIO 讲解 Socket

  1. 早期的 Java 网络相关的 API(java.net包) 使用 Socket(套接字)进行网络通信,不过只支持阻塞函数使用
  2. ServerSocket accept() 方法是阻塞方法,也就是说 ServerSocket 在调用 accept()等待
  3. 客户端的连接请求时会阻塞,直到收到客户端发送的连接请求才会继续往下执行代码,
  4. 因此我们需要要为每个 Socket 连接开启一个线程(可以通过线程池来做)。
  5. 但使用线程池十分消耗资源

image.png
image.png

4:NIO 讲解

  1. NIO 是一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入了 NIO 框架,
  2. 对应 java.nio 包,提供了 Channel , SelectorBuffer 等抽象。
  3. NIO 提供了与传统 BIO 模型中的 Socket ServerSocket
  4. 相对应的 SocketChannel ServerSocketChannel 两种不同的套接字通道实现,
  5. 两种通道都支持阻塞和非阻塞两种模式:
  6. 1:阻塞模式 : 基本不会被使用到。使用起来就像传统的网络编程一样,比较简单,但是性能和可靠性都不好。
  7. 对于低负载、低并发的应用程序,勉强可以用一下以提升开发速率和更好的维护性
  8. 2:非阻塞模式 与阻塞模式正好相反,非阻塞模式对于高负载、高并发的(网络)应用来说非常友好,
  9. 但是编程麻烦,这个是大部分人诟病的地方。所以, 也就导致了 Netty 的诞生。
  10. NIO 核心组件解读
  11. Channel Buffer Selector Selection Key
  12. 1NIO 使用 Channel(通道)和 Buffer(缓冲区)传输数据,数据总是从缓冲区写入通道,并从通道读取到缓冲区。
  13. 在面向流的 I/O 中,可以将数据直接写入或者将数据直接读到 Stream 对象中。
  14. NIO 库中,所有数据都是通过 Buffer(缓冲区)处理的。
  15. Channel 可以看作是 Netty 的网络操作抽象类,对应于 JDK 底层的 Socket
  16. 2NIO 利用 Selector (选择器)来监视多个通道的对象,如数据到达,连接打开等。因此,单线程可以监视多个通道中的数据。
  17. 3:当我们将 Channel 注册到 Selector 中的时候, 会返回一个 Selection Key 对象,
  18. Selection Key 则表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。
  19. 通过 Selection Key 我们可以获取哪些 IO 事件已经就绪了,并且可以通过其获取 Channel 并对其进行操作。
  20. Selector(选择器,也可以理解为多路复用器)是 NIO(非阻塞 IO)实现的关键。
  21. 不断进行轮训检测 Channel
  22. 它使用了事件通知相关的 API 来实现
  23. 选择已经就绪也就是能够进行 I/O 相关的操作的任务的能力。
  24. 1:将 Channel 注册到 Selector 中。
  25. 2Selector不断轮训 进行Channel的检测
  26. 3:当检测到注册在 Selector 中的某个 Channel 有新的 TCP 连接或者可读写事件的话,
  27. 这个 Channel 就会处于就绪状态(此时创建线程),会被 Selector 轮询出来。
  28. 4:调用 Selector select() 方法,这个方法会阻塞;
  29. 5:然后通过 SelectionKey 可以获取就绪 Channel 的集合,进行后续的 I/O 操作。

image.png

  1. Netty 是一个基于 NIO client-server(客户端服务器)框架,使用它可以快速简单地开发网络应用程序。
  2. 请阅读Netty文集的相关文章 https://www.jianshu.com/nb/49815762

5:AIO讲解

  1. 由于对于IO操作来说,基本上的业务场景都是需要同步的,因此AIO暂时先略过