在进入主题之前先看个 Java 网络编程的一个简单例子:代码很简单,客户端和服务端进行通信,对于客户端的每次输入,服务端回复 get。注意,服务端可以同时允许多个客户端连接。

服务端端代码:

// 创建服务端 socket
ServerSocket serverSocket = new ServerSocket(20000);
client = serverSocket.accept();

// 客户端连接成功,输出提示
System.out.println("客户端连接成功");
// 启动一个新的线程处理客户端请求
new Thread(new ServerThread(client)).start();

// 子线程中处理客户端的输入
class ServerThread implements Runnable {
    .....
    @Override
    public void run() {
        boolean flag = true;
        while (flag) {

            // 读取客户端发送来的数据
            String str = buf.readLine();

            // 回复给客户端 get 表示收到数据
            out.println("get");
        }
    }
}

客户端代码 :

Socket client = new Socket("127.0.0.1", 20000);
boolean flag = true;
while (flag) {

    // 读取用户从键盘的输入
    String str = input.readLine();
    // 把用户的输入发送给服务端
    out.println(str);

    // 接受到服务端回传的 get 字符串
    String echo = buf.readLine();
    System.out.println(echo);
    }
}

考虑到完整的 Java 示例代码太过庞大影响阅读,所以这里不完整贴出,如果需要在 github 直接下载,这里是下载地址

可以看到,server 为了能够同时处理多个 client 的请求,需要为每个 client 开启一个 thread,这种 one-thread-per-client 的模式对于 server 而言压力是很大的。假设有 1k 个 client,对应的 server 应该启动 1k 个 thread,那么 server 所耗费的内存,以及 thread 切换时候占用的时间等等都是致命伤。即使使用线程池的技术来限制线程个数,这种 blocking-IO 的模型还是没办法支撑大量连接。

每个 client 都需要一个 thread 来请求处理。

NIO

上面这种 one-thread-per-client 的模式无法支撑大量连接的主要原因在于 readLine 会 阻塞 IO,即在 readLine 没能够读取到数据的时候,会一直阻塞线程,使得线程无法继续执行,那么 server 为了可以同时处理多个 client,只能同时开启多个线程。

所以,Java 1.4 之后引入了一套 NIO 接口。NIO 中最主要的一个功能就是可以进行非阻塞 IO 操作:如果没能够读取到数据,非阻塞 IO 不会阻塞线程,而是直接返回 0。这种情况下,线程通过返回值判断数据还没有准备好,就可以处理其他事情,而不会被阻塞。

上图是阻塞 IO 和非阻塞 IO 的区别,可以看出虽然 非阻塞 IO 并不会被阻塞,但是它仍然不断的调用函数检查数据是否已经可读,这种现象在代码中是以这种形式展现:

while((str = read()) == 0) {

}
 // 继续读取到数据之后的逻辑。

可以明白,虽然非阻塞 IO 不会阻塞线程,但是由于没有数据可读,线程也没有办法继续执行下面的逻辑,只能不断的调用判断,等待数据到来。这种情况下称为同步 IO。所以综上,NIO 本质上是一个非阻塞同步 IO。

IO 复用

由于 NIO 不会因为数据还没有到达而被阻塞,那么就没有必要每一个 client 都分配一个 thread 不断去轮询判断是否有数据可读。可以使用一个 thread 监听所有的 client 连接,由这个 thread 循环判断是否有某个 client 的数据可读,如果有就告知其他 thread 某个 client 连接由数据可读。这种行为就被称之为 IO 复用。 在 NIO 中提供了 Selector 类来监听所有 client 连接是否有数据可读。

使用 Selector 来实现 IO 复用,只有一个 thread 需要关心数据是否到来,其他线程等待通知就好。如此一来,只有监听线程会一直循环判断,并不会占据太多 CPU 资源。提到 NIO 中的 Selector,不得不说一下 Linux 编程中的 IO 复用,因为 NIO 中的 Selector 底层就是使用系统级的 IO 复用方案。

Linux 系统的 IO 复用实现方案有 2 种:

  • select
  • epoll

在 Linux 2.6+ 的版本上 NIO 底层使用的是 epoll,在 2.4.x 的版本使用的是 select 函数。epoll 函数在性能方面比 select好很多,这里可以不关心 Linux 编程具体细节。值得一提的是,Java 的 netty 网络框架底层就是使用 NIO 技术。

AIO

回顾一下 NIO 中:使用监听线程调用 select 函数来监听所有请求是否有数据到达,如果有数据则通知其他线程来读取数据。这里在线程读取数据的过程中,线程在数据没有读取完毕之前是处于阻塞状态,只有数据读取完毕之后线程才可以继续执行逻辑。之前说过,这种称之为同步 IO。JDK 7 中新增了一套新接口 AIO(Asynchronous IO)。

AIO 有一个神奇的特性:当发起 IO 操作之后,线程不用等待 IO 读取完毕,而是可以直接返回,继续执行其他操作。等到数据读取完毕之后,系统会通知线程数据已经读取完毕。这种发起 IO 操作,但是不必等待数据读取完毕的 IO 操作称之为异步 IO。如果使用 AIO,一个线程可以同时发起多个 IO 操作,这就意味着,一个线程可以同时处理多个请求。著名的 web 服务器 Nginx 就是用了异步 IO。关于更多的细节,可以参考下我的另一篇文章 <Apache--MPMs && Nginx事件驱动>。

End

到目前为止,文章解释了阻塞/非阻塞 IO,同步/异步 IO 的区别,谈起 IO 模型,不可避免会涉及 Linux 的 5 种 IO 模型

  • 阻塞 IO
  • 非阻塞 IO
  • IO 复用
  • 信号驱动 IO
  • 异步 IO

除去信号驱动 IO没有提及,其他 4 种主要的 IO 模型都有所解释,理解了这些 IO 模型的概念对于编写代码有很大的帮助。

Java学习交流QQ群:589809992  禁止闲聊,非喜勿进!

Java 网络 IO 模型的更多相关文章

  1. 通过实例理解Java网络IO模型

    网络IO模型及分类 网络IO模型是一个经常被提到的问题,不同的书或者博客说法可能都不一样,所以没必要死抠字眼,关键在于理解. Socket连接 不管是什么模型,所使用的socket连接都是一样的. 以 ...

  2. Java网络编程和NIO详解3:IO模型与Java网络编程模型

    Java网络编程和NIO详解3:IO模型与Java网络编程模型 基本概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32 ...

  3. Socket-IO 系列(一)Linux 网络 IO 模型

    Socket-IO 系列(一)Linux 网络 IO 模型 一.基本概念 在正式开始讲 Linux IO 模型前,先介绍 5 个基本概念. 1.1 用户空间与内核空间 现在操作系统都是采用虚拟存储器, ...

  4. python网络编程——网络IO模型

    1 网络IO模型介绍 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:    (1)同步阻塞IO(Blocking IO):即传统的IO模型.    (2)同步非阻塞IO(Non-bl ...

  5. 从操作系统层面理解Linux下的网络IO模型

    I/O( INPUT OUTPUT),包括文件I/O.网络I/O. 计算机世界里的速度鄙视: 内存读数据:纳秒级别. 千兆网卡读数据:微妙级别.1微秒=1000纳秒,网卡比内存慢了千倍. 磁盘读数据: ...

  6. 高并发之网络IO模型

    你好,我是坤哥 今天我们聊一下高并发下的网络 IO 模型 高并发即我们所说的 C10K(一个 server 服务 1w 个 client),C10M,写出高并发的程序相信是每个后端程序员的追求,高并发 ...

  7. [编织消息框架][网络IO模型]BIO

    既然跟网络内容有关就不得不学习网络IO模型,时代在进步,技术也在进步,采取使用那种网络IO模型就已经确定应用程序规模 阻塞IO(blocking IO) 在linux中,默认情况下所有的socket都 ...

  8. 5种网络IO模型

    5种网络IO模型(有图,很清楚)   同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到 ...

  9. 【转】5种网络IO模型

    5种网络IO模型(有图,很清楚) IO多路复用—由Redis的IO多路复用yinch Linux中对文件描述符的操作(FD_ZERO.FD_SET.FD_CLR.FD_ISSET

随机推荐

  1. DOM-----style属性对照表

    CSS和Java script 标签对照表 盒子标签和属性对照 CSS语法 (不区分大小写) Java script 语法 (区分大小写) border border border-bottom bo ...

  2. Springboot+resteasy定时任务

    定时任务 需求:按每日统计点赞数.评论数.热度的增加量(不是现有量) 1.每天零点执行:首先遍历出user的统计字段 然后插入到新创建的表中. 2.每天一点执行:根据时间段将两表的数据相减创建增量字段 ...

  3. 网络唤醒全攻略(Wake On Lan)

    家里组了台服务器存放资料,或者作为开发服务器,远程登陆成为刚性需求,由于机器需要的时候才用到,所以如果经常开机的话很费电,按需开机是最佳办法:网上教程很多,但是比较杂乱,表达累赘:还是自己总结一篇简单 ...

  4. 【JSP】JSP Action动作标签

    Action动作标签简述 JSP action是JSP技术体系内置的一组标签,使用无需导入,或者添加另外的库.JSP action标签都是对Java代码的逻辑的封装.主要使用的是下面这些. 标签 作用 ...

  5. 计算机程序的思维逻辑 (95) - Java 8的日期和时间API

    ​本节继续探讨Java 8的新特性,主要是介绍Java 8对日期和时间API的增强,关于日期和时间,我们在之前已经介绍过两节了,32节介绍了Java 1.8以前的日期和时间API,主要的类是Date和 ...

  6. Spring Aop 应用实例与设计浅析

    0.代码概述 代码说明:第一章中的代码为了突出模块化拆分的必要性,所以db采用了真实操作.下面代码中dao层使用了打印日志模拟插入db的方法,方便所有人运行demo. 1.项目代码地址:https:/ ...

  7. JavaScript实现隔行换颜色

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...

  8. chrome开发工具指南(六)

    检查和编辑页面与样式 使用 Chrome DevTools 的 Elements 面板检查和实时编辑页面的 HTML 与 CSS. 在 Elements 面板中检查和实时编辑 DOM 树中的任何元素. ...

  9. Java课设--俄罗斯方块Tetris

    Java程序设计课程作业报告 作业:俄罗斯方块游戏 姓名 赵璐媛 学号 程序得分 90% 作业报告 得分10% 实验总分 100% 作业目的: 掌握基本的图形程序设计方法 掌握Java事件处理程序编写 ...

  10. Java中equals和==之间的区别

    今天在写表达式求值的时候,发现了equals和==||!=和!equals()之间是不一样的. 我就从网上搜了搜关于这方面的知识,然后在下面做一个总结: Java中有两类数据类型: 基本数据类型(Pr ...