NIO 线程模型
什么是 NIO 线程模型?
上图是 NIO 的线程模型, 基于 select 实现, 这种线程模型的特点: 多条 channel 通过一个选择器和单挑线程绑定, 并且在这种编程模型中, Channel 中相关业务逻辑不允许存在耗时的任务 , 如果一定会有耗时的逻辑, 请将它们放置到线程池中去运行, 因为这种模型虽然做到了非阻塞, 但是他并不是真正的异步编程, 任何 channel 上的任何耗时的操作, 都会拖垮这个选择器, 进而拖垮整条线程 , 这也是为啥它会被称为 同步非阻塞
什么是同步?
其一: 因为当 channel 中出现了耗时的操作时, 其他的 channel 不得不同步等待
其二: 从编码上看: NIO 编程中 服务端的 select() 会同步等待选择器感兴趣的事件发生
其三: 从操作系统的角度上看, 程序使用的数据来自 网卡 -> 操作系统的内核缓冲区 -> 用户区, 当数据进入用户区后 java 程序便可以对其进行读写操作, 所谓同步就是: 数据进入用户区的过程中, NIO 编程模型需要同步并不停的询问
NIO 线程模型的优点
NIO 线程线程模型相对于传统的 BIO 来说, 最大的优势就是在于 NIO 线程模型中单条线程可同时为 N 个用户 (Channel) 服务, 而 BIO 编程模型让人诟病的地方就是, 任何一个新连接接入, 服务器都得为他开启不止一条新的线程去运行它, 这种 BIO 系统中, 并发肯定不会很高
NIO 适用场景:
NIO 方式适用于连接数目多且连接比较短 (轻操作) 的架构, 比如聊天服务器, 并发局限于应用中, 编程比较复杂, JDK1.4 开始支持
AIO (Asynchronous Input/Output )模型
什么是 AIO?
AIO 是(jdk1.7) 发行的 异步 IO 编程模型, 真正实现了异步 IO, 基于 Linux 系统的 Epoll 机制实现
无论是 NIO, 还是 AIO 底层都没有改变网络通信的基本步骤, 而是在这个基础上进行了一系列的升级
AIO 的底层实现是由操作系统完成的, 数据在内核空间 & 用户空间的迁移, 我们在编写代码时也是这样, 只需要调用 AIO.read() 或者是 AIO.write() 即可, 换句话说, 我们的业务逻辑就成了 回调, 原来在操作系统处理数据的这个过程中, 我们的程序需要阻塞等待着, 亦或者放在线程池中运行, 而在 AIO 编程中这段等待时间差被省去了, 因为当操作系统认为数据还有没准备完时, 它是不会打扰我们的程序的, 这时我们的程序可以去处理其他的逻辑, 而一旦操作系统认为数据齐全了, 他就会回调我们的提供的回调函数
对应操作系统来说, 当有流数据可读时, 操作系统会将流传入到 read 方法的缓冲区, 然后回调相关的 CompletionHandler
对于写操作而言, 操作系统会将程序中 Buffer 里面数据写入到从用户空间写入到系统空间 再写入到网卡中, 写入完毕, 同样会回调相关的回调函数
AIO 编程 Server 端的示例
下面贴出来一个 AIO 编程 Server 端的实例:
像下面的 read() write() accept() 全是异步的, 一经调用即刻返回, 不一样的地方是我们会提供一个回调对象, 留给操作系统, 当操作系统认为读写数据都到位了, 就会去回调这些函数
- public class AIOServer {
- private ExecutorService executorService;
- // 服务端的 Channel
- private AsynchronousServerSocketChannel asynchronousServerSocketChannel;
- private AIOServer(int port) {
- init(port);
- }
- // 初始化
- private void init(int port) {
- System.out.println("aio server start with port" + port);
- executorService = Executors.newFixedThreadPool(5);
- try {
- // 开启服务端的通道
- asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
- // 绑定端口
- asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
- System.out.println("server start ...");
- /**
- * 方法会异步的去接收一个请求, accept()同样是
- * 参数 1 : this , 暂时理解成任意类型的
- * 参数 2 : CompleteHandler -- 当请求到来后, 会交付给 AIOServerHandler 进行处理
- *
- * todo 在 AIO 中的监听并不是 while(true), 而是类似递归的操作, 每次监听到客户端的请求后, 都需要在处理逻辑中开启下一次的监听
- */
- asynchronousServerSocketChannel.accept(this, new AIOServerHandler());
- System.out.println("------------------------------");
- // 阻塞程序
- try {
- TimeUnit.SECONDS.sleep(60);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public AsynchronousServerSocketChannel getAsynchronousServerSocketChannel() {
- return this.asynchronousServerSocketChannel;
- }
- public static void main(String[] args) {
- AIOServer aioServer = new AIOServer(9999);
- }
- }
AIO 的适用场景
AIO 方式使用于连接数目多且连接比较长 (重操作) 的架构, 比如相册服务器, 充分调用 OS 参与并发操作, 编程比较复杂, JDK7 开始支持.
我是 bloger 赐我白日梦, 欢迎关注我 -- 武汉加油
来源: https://www.cnblogs.com/ZhuChangwu/p/12237223.html