您的位置  > 互联网

java中的阻塞式方法(IO)分类针对操作

1.IO分类

操作对象的粗略分类

磁盘 I/O 网络 I/O 内存映射 I/ I/O 数据库 I/O

Java相关类别

基于字节/基于字符/基于磁盘的文件 I/O 基于网络的 I/O

阻塞和同步分类

BIO(同步&阻塞) NIO(同步&非阻塞) AIO(异步) 2、IO中常见的类,字节流、字符流、接口、实现类、方法阻塞?

输入流主要是从外部文件输入到内存,输出流主要是从内存输出到文件。

IO 中的常用类。 第一印象只知道IO流有很多类。 IO流主要分为字符流和字节流。 字符流中有抽象类及其子类等等。字符流等等。 全部实现,,这些接口。 程序中的输入和输出都是以流的形式保存的,流中保存的实际上是字节文件。

Java中的阻塞方法是指当程序调用某个方法时,必须等待输入数据可用或者检测输入结束或者抛出异常。 否则,程序将停留在该语句上,不会执行后面的语句。 比如read()和()方法。

3、poll和epoll的工作原理

、poll、epoll都是I/O复用机制。 I/O 多路复用可以监视多个描述符。 一旦描述符准备好(通常是读准备好或写准备好),它就可以通知程序执行相应的读写操作。 不过poll和epoll本质上都是同步I/O,因为它们都需要在读写事件就绪后负责读写,也就是说读写过程是阻塞的,而异步I/O则不会需要对自己负责。 对于读取和写入,异步 I/O 实现将负责将数据从内核复制到用户空间。

3.1.

首先创建事件描述符集合。 对于一个描述符,可以关注其上的读事件、写事件和异常事件,因此需要创建三类事件的描述符集合,分别收集读事件描述符、写事件描述符和异常事件描述符。 调用时,首先将时间描述符集合从用户空间复制到内核空间; 注册一个回调函数并遍历所有fd,调用其poll方法。 当 poll 方法返回时,它将返回一个掩码,描述读写操作是否准备就绪。 根据这个 mask 给 fd 赋值。 如果遍历完所有fd后仍然没有准备好读写的mask,则进程将进入休眠状态; 如果超过超时时间还没有被唤醒,则调用进程将被唤醒并获得CPU。 ,再次遍历fd,判断是否有就绪的fd; 最后,它会从内核空间复制回用户空间。

缺点:

每次调用都需要将 fd 集合从用户模式复制到内核模式。 当fd很多的时候这个开销会很大。 同时,每次调用都需要内核遍历所有传入的fd。 当fd很多的时候这个开销也会很大。 支持的文件描述符数量较少,默认为1024

伪代码:

struct timeval tv = {.tv_sec = 1, .tv_usec = 0};
ssize_t nbytes;
while(1) {
    FD_ZERO(&read_fds);
    setnonblocking(fd1);
    setnonblocking(fd2);
    FD_SET(fd1, &read_fds);
    FD_SET(fd2, &read_fds);
    // 把要监听的fd拼到一个数组里,而且每次循环都得重来一次...
    if (select(FD_SETSIZE, &read_fds, NULL, NULL, &tv) < 0) { // block住,直到有事件到达
        perror("select出错了");
        exit(EXIT_FAILURE);
    }
    for (int i = 0; i < FD_SETSIZE; i++) {
        if (FD_ISSET(i, &read_fds)) {
            /* 检测到第[i]个读取fd已经收到了,这里假设buf总是大于到达的数据,所以可以一次read完 */
            if ((nbytes = read(i, buf, sizeof(buf))) >= 0) {
                process_data(nbytes, buf);
            } else {
                perror("读取出错了");
                exit(EXIT_FAILURE);
            }
        }
    }
}

3.2. 轮询

poll 是 . 使用 poll 结构而不是 。 您需要为读事件、写事件和异常事件创建描述符集合。 轮询时,需要分别轮询这三个集合。 poll库只需要创建一个集合,并在每个描述符对应的结构体上设置读事件、写事件或异常事件。 在最终的民意调查中,您可以检查这三类事件是否同时发生。

3.3.epoll

在 poll 和 poll 中,都会创建待处理事件的列表。 然后将这个列表发送给内核,然后返回时轮询这个列表以确定事件是否发生。 当描述符较多时,效率极低。 epoll 将文件描述符列表的管理交给内核。 每次注册新事件时,fd 仅复制到内核。 epoll保证整个过程中fd只被复制一次,避免了重复复制重复fd的巨大开销。 另外,一旦事件发生,内核会将事件发生所在的描述符列表通知给进程,避免轮询所有描述符列表。 最后,epoll 没有文件描述符限制。 fd上限是系统可以打开的最大文件数,通常远大于2048。

伪代码:

#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int nfds, epfd, fd1, fd2;
// 假设这里有两个socket,fd1和fd2,被初始化好。
// 设置为non blocking
setnonblocking(fd1);
setnonblocking(fd2);
// 创建epoll
epfd = epoll_create(MAX_EVENTS);
if (epollfd == -1) {
    perror("epoll_create1");
    exit(EXIT_FAILURE);
}
//注册事件
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = fd1;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd1, &ev) == -1) {
    perror("epoll_ctl: error register fd1");
    exit(EXIT_FAILURE);
}
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd2, &ev) == -1) {
    perror("epoll_ctl: error register fd2");
    exit(EXIT_FAILURE);
}
// 监听事件
for (;;) {
    nfds = epoll_wait(epdf, events, MAX_EVENTS, -1);
    if (nfds == -1) {
        perror("epoll_wait");
        exit(EXIT_FAILURE);
    }
    for (n = 0; n < nfds; ++n) { // 处理所有发生IO事件的fd
        process_event(events[n].data.fd);
        // 如果有必要,可以利用epoll_ctl继续对本fd注册下一次监听,然后重新epoll_wait
    }
}

3.4. poll和epoll的比较

,poll实现需要不断轮询所有fd集合,直到设备准备好,期间可能需要在睡眠和唤醒之间交替多次。 事实上,也需要调用epoll来不断轮询就绪链表,期间可能会出现多次睡眠和唤醒交替。 但是当设备就绪时,它会调用回调函数,将就绪的fd放入就绪链表中,并唤醒其中进入睡眠状态的进程。 虽然都是sleep和,但是poll和poll在awake时都需要遍历整个fd集合,而epoll在awake时只需要判断就绪链表是否为空,这样就节省了大量的CPU时间。

3.5. poll和epoll各自的应用场景:参数精度为1ns,而poll和epoll为1ms,因此更适合实时性要求较高的场景,比如核反应堆的控制。 它更便携,几乎所有主要平台都支持。 poll:poll 对描述符的最大数量没有限制。 如果平台支持且实时性要求不高,则应使用poll:仅运行在Linux平台上,有大量描述符需要同时轮询,这些连接最好这是一个长连接; 如果需要同时监控少于1000个描述符,则不需要使用epoll,因为epoll的优势在这种应用场景中无法体现; 需要监控的描述符的状态变化很大,而且都是非常短暂的,所以没有必要使用epoll。 因为epoll中的所有描述符都保存在内核中,所以每次需要改变描述符的状态时都需要通过()进行系统调用。 频繁的系统调用会降低效率。 并且epoll的描述符存储在内核中,不便于调试。 4. BIO、NIO、AIO工作原理 4.1、BIO

BIO,全称Block-IO,是一种同步阻塞通信模式。 BIO是一种比较传统的通讯方式,模式简单,使用方便。 但并发处理能力低、通信耗时、依赖网络速度。 服务器负责通过线程监听客户端请求,并为每个客户端创建一个新的线程进行链接处理。 典型的请求-答复模式。 如果客户端数量增多,频繁的创建和销毁线程会给服务器带来很大的压力。 后来改进为使用线程池,而不是添加新线程,称为伪异步IO。 BIO 模型中传递和完成套接字通道的实现。 BIO 是阻塞和同步的。

4.2、蔚来

NIO 代表 New IO,也称为 Non-Block IO。 它是一种非阻塞同步通信模式。 NIO 相对于 BIO 来说是一个很大的改进。 客户端和服务器之间的通信。 NIO可以执行读和写操作。 这些将被注册在多路复用器上。 这些是通过线程连续轮询的。 找出哪些 IO 操作已准备好执行。 NIO通过线程轮询的方式实现千万级的客户端请求。 这就是非阻塞NIO的特性。 蔚来的特点如下:

:是NIO和BIO的一个重要区别。 BIO 将数据直接写入或读取到对象中。 NIO的数据操作都是在缓冲区中进行的。 缓冲区实际上是一个数组。 最常见的类型是,还有,,,,,,通道:与流不同,通道是双向的。 NIO可以同时读、写和读写数据(全双工?)。 通道主要分为两大类:一类用于网络读写,一类用于文件操作多路复用器:NIO编程的基础。 多路复用器提供了选择已经准备好的任务的能力。 这意味着注册在其上的通道将被不断轮询()。 如果某个通道处于就绪状态,则会对其进行轮询,然后即可获取就绪集以进行后续的 IO 操作。 服务器端只要提供负责一个线程的轮询,就可以访问数千个客户端。 这是JDK NIO库以及NIO模型中通道实现的巨大进步。非阻塞/阻塞、同步,避免了TCP连接建立时使用三向握手带来的开销

伪代码:

struct timespec sleep_interval{.tv_sec = 0, .tv_nsec = 1000};
ssize_t nbytes;
while (1) {
    /* 尝试读取 */
    if ((nbytes = read(fd, buf, sizeof(buf))) < 0) {
        if (errno == EAGAIN) { // 没数据到
            perror("nothing can be read");
        } else {
            perror("fatal error");
            exit(EXIT_FAILURE);
        }
    } else { // 有数据
        process_data(buf, nbytes);
    }
    // 处理其他事情,做完了就等一会,再尝试
    nanosleep(sleep_interval, NULL);
}

4.3、一体机

AIO也称为NIO2.0,是一种非阻塞异步通信模式。 基于NIO,引入了新的异步通道概念,并提供了异步文件通道和异步套接字通道的实现。 AIO没有使用NIO的多路复用器,而是使用异步通道的概念。 其读写方法的返回类型都是对象。 并且该模型是异步的。 在AIO模型中,通道是通过和来实现的。 AIO 是非阻塞且异步的。

从编程模型的角度来看,AIO和NIO的区别在于NIO需要用户线程不断轮询IO对象来确定数据是否准备好读取。 AIO在数据准备好之前不会通知数据使用者,所以使用者不需要持续轮询。 当然,AIO的异步特性并不是Java实现的伪异步,而是系统底层API支持的。 Unix系统下,使用epoll IO模型,使用IOCP模型。

4.4. BIO vs NIO BIO是面向流(字节流和字符流)的操作,而NIO是面向缓冲区的操作。 BIO是阻塞IO,而NIO是非阻塞。 IOBIO 不支持选择器,而 NIO 支持选择器。

同步阻塞IO:用户进程发起IO操作后,必须等待IO操作实际完成后才能继续运行。 同步非阻塞IO:用户进程发起IO操作后,可以做其他事情,但用户进程需要频繁询问IO操作是否完成。 完成了,这就造成了CPU资源不必要的浪费。 异步非阻塞IO:用户进程发起IO操作,然后立即返回。 当IO操作真正完成后,会通知应用程序IO操作完成。 4.5. AIO vs NIO NIO需要使用用户线程不断轮询IO对象来判断数据是否准备好并且可读。 AIO在数据准备好时通知用户,避免用户不断轮询。 5. 往期文章5.1、访谈系列

1.一边自我介绍一边调侃面试官

2. 用项目介绍打败面试官

3.在系统架构设计上击败面试官

4.询问面试官你负责哪一部分

5. 让其中一位面试官打他一巴掌

6. 你对戏弄面试官有什么疑问吗?

······持续更新······

5.2. 科技系列

1.分布式对话打败面试官

2. 击败面试官的分布式锁

3.打败面试官的乐观情绪

4. 逗弄面试官的幂等问题

5.分布式交易克服面试障碍

6.解决网上那些蔑视面试官的项目问题

······持续更新······

5.3. 源码系列

1、源码分析启动过程原理

2.源码分析及自动汇编原理

3.源码分析容器

4.源码分析容器

5.源码分析容器

6.源码分析容器

7、源码分析五种Map容器​​的区别

······持续更新······

5.4. 数据结构与算法系列

1.数据结构的八种主要数据结构

2、数据结构的动态搜索树(二叉搜索树、平衡二叉树、红黑树)

······持续更新······

5.5. 并发系列

1.并发系列中多线程的首次介绍

2.并发系列的JMM内存模型

3. 并发系列分析

4. 并发系列分析

5. 并发系列和并发系列的区别

6.并发系列的锁分析

7.并发系列和锁的区别

8.并发系列CAS和原子操作

9.并发系列的AQS分析

10.并发系列的线程池分析

11、并发系列锁知识整理

······持续更新······

5.6. 面试题系列

1、面试题系列中并发面试题

2、面试题系列中的在线面试题

······持续更新······

5.7、JVM系列

1.JVM系列中JVM简介

2、JVM系列的类文件分析

3、JVM系列的Java类加载机制

4. JVM系列的JVM内存结构

······持续更新······