ljlao ljlao
首页
ljlao总结
  • Java8
  • Java IO
  • Java基础
  • java基础01
  • java基础02
  • java基础03
  • Java虚拟机
  • Java集合
  • Servlet
  • Java高并发
  • 多线程
  • 并发编程面试专栏
  • 数据结构与算法
  • 操作系统
  • 编译原理
  • 计算机网络
  • Shiro
  • MyBatis
  • Spring
  • Spring Boot
  • Spring Cloud
  • RabbitMQ
  • RocketMQ
  • Kafka
  • Elasticsearch
  • Netty
  • Dubbo
  • ZooKeeper
  • 消息队列
  • 分布式
  • 微服务
  • 数据库
  • MySQL
  • Mycat
  • Redis
  • MongoDB
  • Cassandra
  • Hbase
首页
ljlao总结
  • Java8
  • Java IO
  • Java基础
  • java基础01
  • java基础02
  • java基础03
  • Java虚拟机
  • Java集合
  • Servlet
  • Java高并发
  • 多线程
  • 并发编程面试专栏
  • 数据结构与算法
  • 操作系统
  • 编译原理
  • 计算机网络
  • Shiro
  • MyBatis
  • Spring
  • Spring Boot
  • Spring Cloud
  • RabbitMQ
  • RocketMQ
  • Kafka
  • Elasticsearch
  • Netty
  • Dubbo
  • ZooKeeper
  • 消息队列
  • 分布式
  • 微服务
  • 数据库
  • MySQL
  • Mycat
  • Redis
  • MongoDB
  • Cassandra
  • Hbase
  • Java基础

    • java基础01
    • java基础02
    • java基础03
    • Java虚拟机
    • Java集合
    • Java高并发
    • Servlet
    • 多线程
    • 并发编程面试专栏
    • 操作系统
    • 数据结构与算法
    • 编译原理
    • 计算机网络
    • 设计模式
    • Java IO
      • 43.Selector-Java面试题
      • 42.Buffer-Java面试题
      • 41.Channel-Java面试题
      • 40.NIO 的非阻塞-Java面试题
      • 39.NIO 的缓冲区-Java面试题
      • 38.JAVA NIO-Java面试题
      • 37.异步 IO 模型-Java面试题
      • 36.信号驱动 IO 模型-Java面试题
      • 35.多路复用 IO 模型-Java面试题
      • 34.非阻塞 IO 模型-Java面试题
      • 33.阻塞 IO 模型-Java面试题
      • IO多路复用的底层原理-Java面试题
      • 缓冲区是什么意思?-Java面试题
      • 通道是个什么意思?-Java面试题
      • 同步、异步、阻塞、非堵塞-Java面试题
      • 阻塞与非阻塞-Java面试题
      • 同步与异步-Java面试题
      • 什么是AIO-Java面试题
      • 什么是NIO-Java面试题
      • 什么是BIO-Java面试题
      • 流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?-Java面试题
      • 什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征?-Java面试题
      • PrintStream、BufferedWriter、PrintWriter的比较?-Java面试题
      • 字节流和字符流的区别?-Java面试题
      • 如何实现 java 序列化?-Java面试题
      • 什么是 java序列化?-Java面试题
      • Java 中有几种类型的流?-Java面试题
    • Java8
    • Java基础
  • java高级

    • MyBatis
    • 消息队列
    • Kafka
    • RabbitMQ
    • Netty
    • Shiro
    • RocketMQ
    • ZooKeeper
    • Dubbo
    • Spring Boot
    • Elasticsearch
    • Spring Cloud
    • Spring
    • 分布式
    • 微服务
  • sql

    • Cassandra
    • MongoDB
    • Mycat
    • MySQL
    • Redis
    • Hbase
    • 数据库
  • 运维

    • Jenkins
    • Docker
    • Kubernetes
    • Linux
    • Maven
    • Nginx
    • Tomcat
    • 云计算
  • 面试题库
  • Java基础
ljlao
2022-03-09
目录

Java IO

[TOC]

# Java IO

# 43.Selector-Java面试题


Selector 类是 NIO 的核心类,Selector 能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。


# 42.Buffer-Java面试题


Buffer,故名思意,缓冲区,实际上是一个容器,是一个连续数组。Channel 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 Buffer。

上面的图描述了从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入 Buffer 中,然后将 Buffer 中的内容写入通道。服务端这边接收数据必须通过 Channel 将数据读入到 Buffer 中,然后再从 Buffer 中取出数据来处理。在 NIO 中,Buffer 是一个顶层父类,它是一个抽象类,常用的 Buffer 的子类有:

ByteBuffer、IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、FloatBuffer、ShortBuffer


# 41.Channel-Java面试题


首先说一下 Channel,国内大多翻译成“通道”。Channel 和 IO 中的 Stream(流)是差不多一个等级的。只不过 Stream 是单向的,譬如:InputStream, OutputStream,而 Channel 是双向的,既可以用来进行读操作,又可以用来进行写操作。

NIO 中的 Channel 的主要实现有:

  1. FileChannel
  2. DatagramChannel
  3. SocketChannel
  4. ServerSocketChannel

这里看名字就可以猜出个所以然来:分别可以对应文件 IO、UDP 和 TCP(Server 和 Client)。下面演示的案例基本上就是围绕这 4 个类型的 Channel 进行陈述的。


# 40.NIO 的非阻塞-Java面试题


IO 的各种流是阻塞的。这意味着,当一个线程调用 read() 或write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 NIO 的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞 IO 的空闲时间用于在其它通道上执行 IO 操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。


# 39.NIO 的缓冲区-Java面试题


Java IO 面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。NIO 的缓冲导向方法不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。


# 38.JAVA NIO-Java面试题


NIO 主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统 IO 基于字节流和字符流进行操作,而 NIO 基于 Channel 和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道

NIO 和传统 IO 之间第一个最大的区别是,IO 是面向流的,NIO 是面向缓冲区的。


# 37.异步 IO 模型-Java面试题


异步 IO 模型才是最理想的 IO 模型,在异步 IO 模型中,当用户线程发起 read 操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个asynchronous read 之后,它会立刻返回,说明 read 请求已经成功发起了,因此不会对用户线程产生任何 block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它 read 操作完成了。也就说用户线程完全不需要实际的整个 IO 操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示 IO 操作已经完成,可以直接去使用数据了。
也就说在异步 IO 模型中,IO 操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,然后发送一个信号告知用户线程操作已完成。用户线程中不需要再次调用 IO 函数进行具体的读写。这点是和信号驱动模型有所不同的,在信号驱动模型中,当用户线程接收到信号表示数据已经就绪,然后需要用户线程调用 IO 函数进行实际的读写操作;而在异步 IO 模型中,收到信号表示 IO 操作已经完成,不需要再在用户线程中调用 IO 函数进行实际的读写操作。

注意,异步 IO 是需要操作系统的底层支持,在 Java 7 中,提供了 Asynchronous IO。


# 36.信号驱动 IO 模型-Java面试题


在信号驱动 IO 模型中,当用户线程发起一个 IO 请求操作,会给对应的 socket 注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用 IO 读写操作来进行实际的 IO 请求操作。


# 35.多路复用 IO 模型-Java面试题


多路复用 IO 模型是目前使用得比较多的模型。Java NIO 实际上就是多路复用 IO。在多路复用 IO模型中,会有一个线程不断去轮询多个 socket 的状态,只有当 socket 真正有读写事件时,才真正调用实际的 IO 读写操作。因为在多路复用 IO 模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket 读写事件进行时,才会使用 IO 资源,所以它大大减少了资源占用。在 Java NIO 中,是通过 selector.select()去查询每个通道是否有到达事件,如果没有事件,则一直阻塞在那里,因此这种方式会导致用户线程的阻塞。多路复用 IO 模式,通过一个线程就可以管理多个 socket,只有当socket 真正有读写事件发生才会占用资源来进行实际的读写操作。因此,多路复用 IO 比较适合连接数比较多的情况。

另外多路复用 IO 为何比非阻塞 IO 模型的效率高是因为在非阻塞 IO 中,不断地询问 socket 状态时通过用户线程去进行的,而在多路复用 IO 中,轮询每个 socket 状态是内核在进行的,这个效率要比用户线程要高的多。

不过要注意的是,多路复用 IO 模型是通过轮询的方式来检测是否有事件到达,并且对到达的事件逐一进行响应。因此对于多路复用 IO 模型来说,一旦事件响应体很大,那么就会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询。


# 34.非阻塞 IO 模型-Java面试题


当用户线程发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error 时,它就知道数据还没有准备好,于是它可以再次发送 read 操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。所以事实上,在非阻塞 IO 模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞 IO不会交出 CPU,而会一直占用 CPU。典型的非阻塞 IO 模型一般如下:

while(true){
data = socket.read();
if(data!= error){
处理数据
break;
}
}

但是对于非阻塞 IO 就有一个非常严重的问题,在 while 循环中需要不断地去询问内核数据是否就绪,这样会导致 CPU 占用率非常高,因此一般情况下很少使用 while 循环这种方式来读取数据。


# 33.阻塞 IO 模型-Java面试题


最传统的一种 IO 模型,即在读写数据过程中会发生阻塞现象。当用户线程发出 IO 请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出 CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除 block 状态。典型的阻塞 IO 模型的例子为:data = socket.read();如果数据没有就绪,就会一直阻塞在 read 方法。


# IO多路复用的底层原理-Java面试题


IO多路复用使用两个系统调用(select/poll/epoll和recvfrom),blocking IO只调用了 recvfrom;select/poll/epoll 核心是可以同时处理多个connection,而不是更快,所以连接 数不高的话,性能不一定比多线程+阻塞IO好,多路复用模型中,每一个socket,设置为nonblocking,阻塞是被select这个函数block,而不是被socket阻塞的。

  1. select机制

客户端操作服务器时就会产生这三种文件描述符(简称fd):writefds(写)、readfds(读)、和 exceptfds(异常)。select会阻塞住监视3类文件描述符,等有数据、可读、可写、出异常 或超 时、就会返回;返回后通过遍历fdset整个数组来找到就绪的描述符fd,然后进行对应的IO操 作。

优点: 几乎在所有的平台上支持,跨平台支持性好

缺点: 由于是采用轮询方式全盘扫描,会随着文件描述符FD数量增多而性能下降。 每次调用 select(),需要把 fd 集合从用户态拷贝到内核态,并进行遍历(消息传递都是从 内核到用户空间) 默认单个进程打开的FD有限制是1024个,可修改宏定义,但是效率仍然慢。

  1. poll机制

基本原理与select一致,只是没有最大文件描述符限制,因为采用的是链表存储fd。

  1. epoll机制

epoll之所以高性能是得益于它的三个函数

  1. epoll_create()系统启动时,在Linux内核里面申请一个B+树结构文件系统,返回 epoll对象,也是一个fd
  2. epoll_ctl() 每新建一个连接,都通过该函数操作epoll对象,在这个对象里面修改添加 删除对应的链接fd, 绑定一个callback函数。
  3. epoll_wait() 轮训所有的callback集合,并完成对应的IO操作

优点: 没fd这个限制,所支持的FD上限是操作系统的最大文件句柄数,1G内存大概支持10万个 句柄 效率提高,使用回调通知而不是轮询的方式,不会随着FD数目的增加效率下降 内核和用户空间mmap同一块内存实现


# 缓冲区是什么意思?-Java面试题


  • Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。在 NIO 中加入 Buffer 对 象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,您将数据直接写入或者 将数据直接读到 Stream 对象中
  • 在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中 的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,您都是将它 放到缓冲区中。
  • 缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。 但是一个缓冲区不 仅仅 是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟 踪系统的读/写进程

ByteBuffer

CharBuffer

ShortBuffer

IntBuffer

LongBuffer

FloatBuffer

DoubleBuffer


# 通道是个什么意思?-Java面试题


  • 通道是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过 一个 Channel 对象(通道)。一个 Buffer 实质上是一个容器对象。发送给一个通道的 所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区 中。

  • Channel是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比 较,通道就像是流。 正如前面提到的,所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道 中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道 中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。


# 同步、异步、阻塞、非堵塞-Java面试题


同/异、阻/非堵塞的组合,有四种类型,如下表:

组 合 方 式 性能分析
同步 非 阻塞 提升 I/O 性能的常用手段,就是将 I/O 的阻塞改成非阻塞方式,尤其在网络 I/O 是长连接,同时传输数据也不是很多的情况下,提升性能非常有效。 这种方式通常能提升 I/O 性能,但是会增加CPU 消耗,要考虑增加的 I/O 性能能不能补偿 CPU 的消耗, 也就是系统的瓶颈是在 I/O 还是在 CPU 上。
异步 阻塞 这种方式在分布式数据库中经常用到,例如在网一个分布式数据库中写一条记录,通常会有一份是同步阻塞的记录,而还有两至三份是备份记录会写到其它机器上,这些 备份记录通常都是采用异步阻塞的方式写 I/O。异步阻塞对网络 I/O 能够提升效率,尤其像上面这种同时写多份相同数据的情况。
异 步非 阻塞 这种组合方式用起来比较复杂,只有在一些非常复杂的分布式情况下使用,像集群之 间的消息同步机制一般用这种 I/O 组合方式。如 Cassandra 的 Gossip 通信机制就是采用异步非阻塞的方式。它适合同时要传多份相同的数据到集群中不同的机器,同时 数据的传输量虽然不大,但是却非常频繁。这种网络 I/O 用这个方式性能能达到最高。

# 阻塞与非阻塞-Java面试题


阻塞与非阻塞主要是从 CPU 的消耗上来说的,阻塞就是 CPU 停下来等待一个慢的操作完成 CPU 才接着完成其它的事。非阻塞就是在这个慢的操作在执行时 CPU 去干其它别的事,等这 个慢的操作完成时,CPU 再接着完成后续的操作。虽然表面上看非阻塞的方式可以明显的提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的 CPU 使用时间能 不能补偿系统的切换成本需要好好评估。


# 同步与异步-Java面试题


同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任 务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可 以保持一致。而异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工 作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是 否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。我们可以用打电话和发短 信来很好的比喻同步与异步操作。


# 什么是AIO-Java面试题


AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以 人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操 作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续 的操作


# 什么是NIO-Java面试题


是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建 多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。


# 什么是BIO-Java面试题


BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是 说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调 用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很 低,容易成为应用性能瓶颈。


# 流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?-Java面试题


  1. 流一旦打开就必须关闭,使用close方法

  2. 放入finally语句块中(finally 语句一定会执行)

  3. 调用的处理流就关闭处理流

  4. 多个流互相调用只关闭最外层的流


# 什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征?-Java面试题


  1. 节点流 直接与数据源相连,用于输入或者输出

  2. 处理流:在节点流的基础上对之进行加工,进行一些功能的扩展

  3. 处理流的构造器必须要 传入节点流的子类


# PrintStream、BufferedWriter、PrintWriter的比较?-Java面试题


  1. PrintStream类的输出功能非常强大,通常如果需要输出文本内容,都应该将输出流包 装成PrintStream后进行输出。它还提供其他两项功能。与其他输出流不同, PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream

  2. BufferedWriter:将文本写入字符输出流,缓冲各个字符从而提供单个字符,数组和字符 串的高效写入。通过write()方法可以将获取到的字符输出,然后通过newLine()进行换 行操作。BufferedWriter中的字符流必须通过调用flush方法才能将其刷出去。并且 BufferedWriter只能对字符流进行操作。如果要对字节流操作,则使用 BufferedInputStream

  3. PrintWriter的println方法自动添加换行,不会抛异常,若关心异常,需要调用 checkError方法看是否有异常发生,PrintWriter构造方法可指定参数,实现自动刷新 缓存(autoflush)


# 字节流和字符流的区别?-Java面试题


字节流读取的时候,读到一个字节就返回一个字节;字符流使用了字节流读到一个或多个字节 (中文对应的字节数是两个,在 UTF-8 码表中是 3 个字节)时。先去查指定的编码表,将查 到的字符返回。字节流可以处理所有类型数据,如:图片,MP3,AVI视频文件,而字符流只 能处理字符数据。只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流。 字节流主要是操作 byte 类型数据,以 byte 数组为准,主要操作类就是 OutputStream、 InputStream字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字 符串,而字节流处理单元为 1 个字节,操作字节和字节数组。所以字符流是由 Java 虚拟机将字 节转化为 2 个字节的 Unicode 字符为单位的字符而成的,所以它对多国语言支持性比较好! 如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好 点。在程序中一个字符等于两个字节,java 提供了 Reader、Writer 两个专门操作字符流的 类。


# 如何实现 java 序列化?-Java面试题


序列化的实现,将需要被序列化的类实现Serializable 接口,该接口没有需要实现的方法, implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如: File Output Stream)来构造一个 Object Output Stream(对象流)对象,接着,使用 Object Output Stream 对象的 write Object(Object obj)方法就可以将参数为 obj 的对象写出(即保 存其状态),要恢复的话则用输入流。


# 什么是 java序列化?-Java面试题


序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流 化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象 流进行读写操作时所引发的问题


# Java 中有几种类型的流?-Java面试题


(1)按照流的方向:输入流(inputStream)和输出流(outputStream);

(2)按照实现 功能分:节点流(可以从或向一个特定的地方(节点)读写数据。如 FileReader)和处理流 (是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其 他流的多次包装,称为流的链接);

(3)按照处理数据的单位: 字节流和字符流。字节流继 承于 InputStream 和 OutputStrea,字符流继承于InputStreamReader 和 OutputStreamWriter 。


上次更新: 2022/03/09, 23:07:05
设计模式
Java8

← 设计模式 Java8→

Theme by Vdoing | Copyright © 2019-2025 鄂公网安备42028102000288 鄂ICP备19019767号-2鄂ICP备19019767号-4
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式