Nginx_BIO_NIO_AIO面试题(2021最新版)

前言

在全部互联网公司中,Nginx 做为最经常使用的 7 层负载均衡代理层,每一个后端开发人员和运维人员都应该对其有较为深刻的理解。java

小编分享的这份金三银四Java后端开发面试总结包含了JavaOOP、Java集合容器、Java异常、并发编程、Java反射、Java序列化、JVM、Redis、Spring MVC、MyBatis、MySQL数据库、消息中间件MQ、Dubbo、Linux、ZooKeeper、 分布式&数据结构与算法等26个专题技术点,都是小编在各个大厂总结出来的面试真题,已经有不少粉丝靠这份PDF拿下众多大厂的offer,今天在这里总结分享给到你们!【持续更新中!】面试

完整版Java面试题地址:2021最新面试题合集集锦算法

序号 专题 内容 连接
1 中间件 Java中间件面试题(2021最新版) https://blog.51cto.com/14994509/2692669
2 微服务 Java微服务面试题(2021最新版) http://www.javashuo.com/article/p-haiifnkb-vk.html
3 并发编程 Java并发编程面试题(2021最新版) http://www.javashuo.com/article/p-rirekprn-vk.html
4 Java基础 Java基础知识面试题(2021最新版) https://blog.51cto.com/blogger/success/2706687
5 Spring Boot Spring Boot面试题(2021最新版) http://www.javashuo.com/article/p-etpzmprr-vk.html
6 Redis Redis面试题(2021最新版) http://www.javashuo.com/article/p-adjrmfwe-vk.html
7 Spring MVC Spring MVC面试题(2021最新版) https://blog.51cto.com/u_14994509/2711562
8 Spring Cloud Spring Cloud面试题(2021最新版) http://www.javashuo.com/article/p-emhafwrm-vk.html
9 MySQL优化 MySQL优化面试题(2021最新版) https://blog.51cto.com/u_14994509/2716504
10 JVM JVM性能调优面试题(2021最新版) https://blog.51cto.com/u_14994509/2716514
11 Linux Linux面试题(2021最新版) https://blog.51cto.com/u_14994509/2718559
12 Mybatis Mybatis面试题(2021最新版) https://blog.51cto.com/u_14994509/2718588
13 网络编程 TCP,UDP,Socket,Http网络编程面试题(2021最新版) https://blog.51cto.com/u_14994509/2718599
14 设计模式 设计模式面试题(2021最新版) https://blog.51cto.com/u_14994509/2735448
15 大数据 大数据面试题100道(2021最新版) https://blog.51cto.com/u_14994509/2736601
16 Tomcat Tomcat面试题(2021最新版) https://blog.51cto.com/u_14994509/2739386
17 多线程 多线程面试题(2021最新版) https://blog.51cto.com/u_14994509/2739435
18 Nginx Nginx_BIO_NIO_AIO面试题(2021最新版) 持续更新中!
19 memcache memcache面试题(2021最新版) 持续更新中!
20 java异常 java异常面试题(2021最新版) 持续更新中!
21 Java虚拟机 Java虚拟机面试题(2021最新版) 持续更新中!
22 Java集合 Java集合面试题(2021最新版) 持续更新中!
23 Git经常使用命令 Git经常使用命令(2021最新版) 持续更新中!
24 Elasticsearch Elasticsearch面试题(2021最新版) 持续更新中!
25 Dubbo Dubbo面试题(2021最新版) 持续更新中!

1. 什么是IO

Nginx_BIO_NIO_AIO面试题(2021最新版)

2. 在了解不一样的IO以前先了解:同步与异步,阻塞与非阻塞的区别

  • 同步,一个任务的完成以前不能作其余操做,必须等待(等于在打电话)
  • 异步,一个任务的完成以前,能够进行其余操做(等于在聊QQ)
  • 阻塞,是相对于CPU来讲的, 挂起当前线程,不能作其余操做只能等待
  • 非阻塞,,无须挂起当前线程,能够去执行其余操做

3. 什么是BIO

BIO:同步并阻塞,服务器实现一个链接一个线程,即客户端有链接请求时服务器端就须要启动一个线程进行处理,没处理完以前此线程不能作其余操做(若是是单线程的状况下,我传输的文件很大呢?),固然能够经过线程池机制改善。BIO方式适用于链接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4之前的惟一选择,但程序直观简单易理解。数据库

4. 什么是NIO

NIO:同步非阻塞,服务器实现一个链接一个线程,即客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到链接有I/O请求时才启动一个线程进行处理。NIO方式适用于链接数目多且链接比较短(轻操做)的架构,好比聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4以后开始支持。 编程

5. 什么是AIO

  • AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由操做系统先完成了再通知服务器应用去启动线程进行处理,AIO方式使用于链接数目多且链接比较长(重操做)的架构,好比相册服务器,充分调用操做系统参与并发操做,编程比较复杂,JDK1.7以后开始支持。.
  • AIO属于NIO包中的类实现,其实IO主要分为BIO和NIO,AIO只是附加品,解决IO不能异步的实现
  • 在之前不多有Linux系统支持AIO,Windows的IOCP就是该AIO模型。可是如今的服务器通常都是支持AIO操做

6. 什么Netty

Nginx_BIO_NIO_AIO面试题(2021最新版)

7. BIO和NIO、AIO的区别

  • BIO是阻塞的,NIO是非阻塞的.
  • BIO是面向流的,只能单向读写,NIO是面向缓冲的, 能够双向读写
  • 使用BIO作Socket链接时,因为单向读写,当没有数据时,会挂起当前线程,阻塞等待,为防止影响其它链接,,须要为每一个链接新建线程处理.,然而系统资源是有限的,,不能过多的新建线程,线程过多带来线程上下文的切换,历来带来更大的性能损耗,所以须要使用NIO进行BIO多路复用,使用一个线程来监听全部Socket链接,使用本线程或者其余线程处理链接
  • AIO是非阻塞 以异步方式发起 I/O 操做。当 I/O 操做进行时能够去作其余操做,由操做系统内核空间提醒IO操做已完成(不懂的能够往下看)

8. IO流的分类

Nginx_BIO_NIO_AIO面试题(2021最新版)

9. 什么是内核空间

Nginx_BIO_NIO_AIO面试题(2021最新版)

10. 五种IO模型

注意:我这里的用户空间就是应用程序空间 后端

10.1 阻塞BIO(blocking I/O)

image

10.2.非阻塞NIO(noblocking I/O)

B也在河边钓鱼,可是B不想将本身的全部时间都花费在钓鱼上,在等鱼上钩这个时间段中,B也在作其余的事情(一会看看书,一会读读报纸,一会又去看其余人的钓鱼等),但B在作这些事情的时候,每隔一个固定的时间检查鱼是否上钩。一旦检查到有鱼上钩,就停下手中的事情,把鱼钓上来。 B在检查鱼竿是否有鱼,是一个轮询的过程。设计模式

Nginx_BIO_NIO_AIO面试题(2021最新版)

10.3.异步AIO(asynchronous I/O)

Nginx_BIO_NIO_AIO面试题(2021最新版)

10.4.信号驱动IO(signal blocking I/O)

G也在河边钓鱼,但与A、B、C不一样的是,G比较聪明,他给鱼竿上挂一个铃铛,当有鱼上钩的时候,这个铃铛就会被碰响,G就会将鱼钓上来。 api

Nginx_BIO_NIO_AIO面试题(2021最新版)

10.5.IO多路转接(I/O multiplexing)

Nginx_BIO_NIO_AIO面试题(2021最新版)

11. 什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别

  • Bit最小的二进制单位 ,是计算机的操做部分取值0或者1
  • Byte是计算机中存储数据的单元,是一个8位的二进制数,(计算机内部,一个字节可表示一个英文字母,两个字节可表示一个汉字。) 取值(-128-127)
  • Char是用户的可读写的最小单位,他只是抽象意义上的一个符号。如‘5’,‘中’,‘¥’ 等等等等。在java里面由16位bit组成Char 取值 (0-65535)
  • Bit 是最小单位 计算机他只能认识0或者1
  • Byte是8个字节 是给计算机看的
  • 字符 是看到的东西 一个字符=二个字节

12. 什么叫对象序列化,什么是反序列化,实现对象序列化须要作哪些工做

  • 对象序列化,将对象以二进制的形式保存在硬盘上
  • 反序列化;将二进制的文件转化为对象读取
  • 实现serializable接口,不想让字段放在硬盘上就加transient

13. 在实现序列化接口是时候通常要生成一个serialVersionUID字段,它叫作什么,通常有什么用

  • 若是用户没有本身声明一个serialVersionUID,接口会默认生成一个serialVersionUID
  • 可是强烈建议用户自定义一个serialVersionUID,由于默认的serialVersinUID对于class的细节很是敏感,反序列化时可能会致使InvalidClassException这个异常。
  • (好比说先进行序列化,而后在反序列化以前修改了类,那么就会报错。由于修改了类,对应的SerialversionUID也变化了,而序列化和反序列化就是经过对比其SerialversionUID来进行的,一旦SerialversionUID不匹配,反序列化就没法成功。

14. 怎么生成SerialversionUID

Nginx_BIO_NIO_AIO面试题(2021最新版)

15. BufferedReader属于哪一种流,它主要是用来作什么的,它里面有那些经典的方法

属于处理流中的缓冲流,能够将读取的内容存在内存里面,有readLine()方法数组

16. Java中流类的超类主要有那些?

  • 超类表明顶端的父类(都是抽象类)
  • java.io.InputStream
  • java.io.OutputStream
  • java.io.Reader
  • java.io.Writer

17. 为何图片、视频、音乐、文件等 都是要字节流来读取

Nginx_BIO_NIO_AIO面试题(2021最新版)

18. IO的经常使用类和方法,以及如何使用

Nginx_BIO_NIO_AIO面试题(2021最新版)

19. IO基本操做讲解

这里的基本操做就是普通的读取操做,若是想要跟深刻的了解不一样的IO开发场景必须先了解IO的基本操做服务器

20. 网络操做IO讲解

  • 我这使用Socket简单的来模拟网络编程IO会带来的问题
  • 不懂Socket能够看我以前的文章,这个东西很容易懂的,就是基于TCP实现的网络通讯,比http要快,不少实现网络通讯的框架都是基于Socket来实现

21. 网络操做IO编程演变历史

21.1 BIO编程会出现什么问题?

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

package com.test.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//TCP协议Socket使用BIO进行通讯:服务端
public class BIOServer {
// 在main线程中执行下面这些代码
public static void main(String[] args) {
//使用Socket进行网络通讯
ServerSocket server = null;
Socket socket = null;
//基于字节流
InputStream in = null;
OutputStream out = null;
try {
server = new ServerSocket(8000);
System.out.println("服务端启动成功,监听端口为8000,等待客户端链接...");
while (true){
socket = server.accept(); //等待客户端链接
System.out.println("客户链接成功,客户信息为:" +
socket.getRemoteSocketAddress());
in = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
//读取客户端的数据
while ((len = in.read(buffer)) > 0) {
System.out.println(new String(buffer, 0, len));
}
//向客户端写数据
out = socket.getOutputStream();
out.write("hello!".getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
TCP协议Socket使用BIO进行通讯:客户端(第二执行)
package com.test.io;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
//TCP协议Socket使用BIO进行通讯:客户端
public class Client01 {
public static void main(String[] args) throws IOException {
//建立套接字对象socket并封装ip与port
Socket socket = new Socket("127.0.0.1", 8000);
//根据建立的socket对象得到一个输出流
//基于字节流
OutputStream outputStream = socket.getOutputStream();
//控制台输入以IO的形式发送到服务器
System.out.println("TCP链接成功 \n请输入:");
String str = new Scanner(System.in).nextLine();
byte[] car = str.getBytes();
outputStream.write(car);
System.out.println("TCP协议的Socket发送成功");
//刷新缓冲区
outputStream.flush();
//关闭链接
socket.close();
}
}
package com.test.io;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
//TCP协议Socket:客户端
public class Client02 {
public static void main(String[] args) throws IOException {
//建立套接字对象socket并封装ip与port
Socket socket = new Socket("127.0.0.1", 8000);
//根据建立的socket对象得到一个输出流
//基于字节流
OutputStream outputStream = socket.getOutputStream();
//控制台输入以IO的形式发送到服务器
System.out.println("TCP链接成功 \n请输入:");
String str = new Scanner(System.in).nextLine();
byte[] car = str.getBytes();
outputStream.write(car);
System.out.println("TCP协议的Socket发送成功");
//刷新缓冲区
outputStream.flush();
//关闭链接
socket.close();
}
}
  • 为了解决堵塞问题,可使用多线程,请看下面

21.2 多线程解决BIO编程会出现的问题

这时有人就会说,我多线程不就解决了吗?

  • 使用多线程是能够解决堵塞等待时间很长的问题,由于他能够充分发挥CPU
  • 然而系统资源是有限的,不能过多的新建线程,线程过多带来线程上下文的切换,历来带来更大的性能损耗

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

package com.test.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//TCP协议Socket使用多线程BIO进行通行:服务端
public class BIOThreadService {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(8000);
System.out.println("服务端启动成功,监听端口为8000,等待客户端链接... ");
while (true) {
Socket socket = server.accept();//等待客户链接
System.out.println("客户链接成功,客户信息为:" +
socket.getRemoteSocketAddress());
//针对每一个链接建立一个线程, 去处理I0操做
//建立多线程建立开始
Thread thread = new Thread(new Runnable() {
public void run() {
try {
InputStream in = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
//读取客户端的数据
while ((len = in.read(buffer)) > 0) {
System.out.println(new String(buffer, 0, len));
}
//向客户端写数据
OutputStream out = socket.getOutputStream();
out.write("hello".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
  • 为了解决线程太多,这时又来了,线程池

21.3 线程池解决多线程BIO编程会出现的问题

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

package com.test.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//TCP协议Socket使用线程池BIO进行通行:服务端
public class BIOThreadPoolService {
    public static void main(String[] args) {
        //建立线程池
        ExecutorService executorService = Executors.newFixedThreadPool(30);
        try {
            ServerSocket server = new ServerSocket(8000);
            System.out.println("服务端启动成功,监听端口为8000,等待客户端链接...");
            while (true) {
                Socket socket = server.accept();
                //等待客户链接
                System.out.println("客户链接成功,客户信息为:" +
                socket.getRemoteSocketAddress());
                //使用线程池中的线程去执行每一个对应的任务
                executorService.execute(new Thread(new Runnable() {
                    public void run() {
                        try {
                            InputStream in = socket.getInputStream();
                            byte[] buffer = new byte[1024];
                            int len = 0;
                            //读取客户端的数据
                            while ((len = in.read(buffer)) > 0) {
                                System.out.println(new String(buffer, 0, len));
                            }
                            //向客户端写数据
                            OutputStream out = socket.getOutputStream();
                            out.write("hello".getBytes());
                        }
                        catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                )
                );
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

21.4 使用NIO实现网络通讯

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

package com.test.io;
import com.lijie.iob.RequestHandler;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
    public static void main(String[] args) throws IOException {
        //111111111
        //Service端的Channel,监听端口的
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        //设置为非阻塞
        serverChannel.configureBlocking(false);
        //nio的api规定这样赋值端口
        serverChannel.bind(new InetSocketAddress(8000));
        //显示Channel是否已经启动成功,包括绑定在哪一个地址上
        System.out.println("服务端启动成功,监听端口为8000,等待客户端链接..."+
        serverChannel.getLocalAddress());
        //22222222
        //声明selector选择器
        Selector selector = Selector.open();
        //这句话的含义,是把selector注册到Channel上面,
        //每一个客户端来了以后,就把客户端注册到Selector选择器上,默认状态是Accepted
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        //33333333
        //建立buffer缓冲区,声明大小是1024,底层使用数组来实现的
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        RequestHandler requestHandler = new RequestHandler();
        //444444444
        //轮询,服务端不断轮询,等待客户端的链接
        //若是有客户端轮询上来就取出对应的Channel,没有就一直轮询
        while (true) {
            int select = selector.select();
            if (select == 0) {
                continue;
            }
            //有可能有不少,使用Set保存Channel
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //使用SelectionKey来获取链接了客户端和服务端的Channel
                SelectionKey key = iterator.next();
                //判断SelectionKey中的Channel状态如何,若是是OP_ACCEPT就进入
                if (key.isAcceptable()) {
                    //从判断SelectionKey中取出Channel
                    ServerSocketChannel channel = (ServerSocketChannel)
                    key.channel();
                    //拿到对应客户端的Channel
                    SocketChannel clientChannel = channel.accept();
                    //把客户端的Channel打印出来
                    System.out.println("客户端通道信息打印:" + clientChannel.getRemoteAddress());
                    //设置客户端的Channel设置为非阻塞
                    clientChannel.configureBlocking(false);
                    //操做完了改变SelectionKey中的Channel的状态OP_READ
                    clientChannel.register(selector, SelectionKey.OP_READ);
                }
                //到此轮训到的时候,发现状态是read,开始进行数据交互
                if (key.isReadable()) {
                    //以buffer做为数据桥梁
                    SocketChannel channel = (SocketChannel) key.channel();
                    //数据要想读要先写,必须先读取到buffer里面进行操做
                    channel.read(buffer);
                    //进行读取
                    String request = new String(buffer.array()).trim();
                    buffer.clear();
                    //进行打印buffer中的数据
                    System.out.println(String.format("客户端发来的消息: %s : %s",
                    channel.getRemoteAddress(), request));
                    //要返回数据的话也要先返回buffer里面进行返回
                    String response = requestHandler.handle(request);
                    //而后返回出去
                    channel.write(ByteBuffer.wrap(response.getBytes()));
                }
                iterator.remove();
            }
        }
    }
}

image

package com.test.io;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
//TCP协议Socket:客户端
public class Client01 {
    public static void main(String[] args) throws IOException {
        //建立套接字对象socket并封装ip与port
        Socket socket = new Socket("127.0.0.1", 8000);
        //根据建立的socket对象得到一个输出流
        OutputStream outputStream = socket.getOutputStream();
        //控制台输入以IO的形式发送到服务器
        System.out.println("TCP链接成功 n请输入:");
        while(true){
            byte[] car = new Scanner(System.in).nextLine().getBytes();
            outputStream.write(car);
            System.out.println("TCP协议的Socket发送成功");
            //刷新缓冲区
            outputStream.flush();
        }
    }
}

21.5 使用Netty实现网络通讯

  • Netty是由JBOSS提供的一个Java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
  • Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 能够确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty至关简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的Socket服务开发。

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)

Nginx_BIO_NIO_AIO面试题(2021最新版)