指点成金-最美分享吧

登录

图解Java IO模型

佚名 举报

篇首语:本文由小编为大家整理,主要介绍了图解Java IO模型相关的知识,希望对你有一定的参考价值。

对于 Java 的 IO 模型,对于大多数小伙伴都不陌生。但是想要准确的讲述这些 IO 模型之间的不同特点以及应用的范围,可能有一部分小伙伴很难讲清楚。在这篇文章中,我将尽可能清楚的讲述这些问题,希望对于坐在屏幕面前的你有一定的帮助。

在这篇文章中,我会先叙述 Linux 的 IO 模型,因为对于一些基本概念同步、异步、阻塞、非阻塞 Linux 和 Java 都是一致的,而且 Linux 的模型和 Java 的 IO 模型有相似的地方,或者说 Java 的 IO 模型有参考 Linux 的 IO 模型的地方。

Linux IO 模型

概念

内核态会涉及到一些比较底层或者比较危险的指令。如果对于用户开放的话,会造成比较危险的状况,所以用户不能直接调用这些指令。用户需要借助系统调用才能执行一些相关的操作。

同步/异步关注的是消息通信机制 。

同步:就是在发出一个调用时,在没有得到结果之前, 该调用就不返回。异步:调用在发出之后,这个调用就直接返回了,所以没有返回结果。

阻塞/非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

阻塞:指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞:指在不能立刻得到结果之前,该调用不会阻塞当前线程。

举个简单的例子。

淘宝双 11 活动,小图同学买了一本《图解 Java》的书,此时快递小哥正在派送快递的情景。

同步、异步

同步、异步一般发生在不同的线程/进程之间。如果调用者发起调用,能马上获得调用结果,这就是异步调用。如果不能马上获得结果,需要等待被调用的线程/进程一段时间,才能获得结果,这就是同步调用。

阻塞、非阻塞

阻塞、非阻塞一般发生在单个进程/线程中。当进程/线程想要进行 A 操作,但是不能立刻完成(需要其他的条件 C)。如果线程/进程一直等待,不执行其他的操作,即此时处于阻塞状态。如果该线程/进程又开始执行另外的操作 B,此时线程处于非阻塞状态。

Linux IO 模型

根据前面描述的内核态和用户态的相关概念。当应用程序发起文件调用的时候,用户态运行的程序必须委托系统调用来访问硬件和内存。这个过程主要分为两个阶段,等待数据将数据从内核拷贝到用户空间。根据这两个阶段中,调用进程和内核进程的表现,分为五种模型。


阻塞 IO 模型:

调用进程会一直阻塞,直到数据拷贝完成 。即应用程序调用一个 IO 函数,导致应用程序阻塞,等待数据准备好。数据准备好后,从内核拷贝到用户空间,IO 函数返回成功指示。在整个调用过程中都是阻塞的。


非阻塞 IO 模型:

调用进程在等待数据的过程中,会不断轮询数据有没有准备好。数据准备好后,从内核拷贝到用户空间,IO 函数返回成功指示。在等待数据的过程中是非阻塞的;将数据从内核拷贝到用户空间是阻塞的。

IO 复用模型:

在非阻塞 IO 模型中,需要调用线程不断去轮询,检查数据有没有准备好,在这个过程中消耗了 CPU 大量的时间。如果有其他的线程帮助去检查多个线程数据的完成状态,这样会提高效率。

Linux 提供了selectpollepoll帮助我们。一个线程可以对多个 IO 端口进行监听,当 socket 有读写事件时分发到具体的线程进行处理。

select、poll、epoll 区别总结:


支持一个进程打开连接数 IO 效率 消息传递方式
select 32 位机器 1024 个,64 位 2048 个 IO 效率低 内核需要将消息传递到用户空间,都需要内核拷贝动作
poll 无限制,原因基于链表存储 IO 效率低 内核需要将消息传递到用户空间,都需要内核拷贝动作
epoll 有上限,但很大,2G 内存 20W 左右 只有活跃的 socket 才调用 callback,IO 效率高 通过内核与用户空间共享一块内存来实现


信号驱动式 I/O:

首先我们允许 Socket 进行信号驱动 IO,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用 I/O 操作函数处理数据。


异步 IO 模型:

相对于同步 IO,异步 IO 不是顺序执行。用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到 socket 数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO 两个阶段,进程都是非阻塞的。


以上是关于图解Java IO模型的主要内容,如果未能解决你的问题,请参考以下文章