• 几个2023年最常见的JAVA面试题
  • 发布于 2个月前
  • 258 热度
    0 评论
问题一:服务端调用listen之后sleep休眠,请问客户端的connect能不能成功?

我们先看看客户端连接服务端时,发生了什么?

流程说明:

服务端和客户端初始化 socket
服务端调用 bind,将 socket 绑定在指定的 IP 地址和端口;
服务端调用 listen,进行监听,这个时候服务端内核就会有 TCP 半连接队列和 TCP 全连接队列了。
客户端调用 connect,向服务端的地址和端口发起连接请求,这时候就会发生 TCP 第一次握手,发送 syn 报文,并告诉服务端当前发送序列号 client_isn,客户端进入 SYN_SENT 状态;
服务端的协议栈收到这个包之后,和客户端进行 ACK 应答,应答的值为 client_isn+1,表示对 SYN 包 client_isn 的确认,同时服务端也发送一个 SYN 包,告诉客户端当前我的发送序列号为 server_isn,服务端进入 SYN_RCVD 状态,然后会把这个连接放到  TCP 半连接队列中。
客户端协议栈收到 ACK 之后,使得应用程序从 connect 调用返回,表示客户端到服务端的单向连接建立成功,客户端的状态为 ESTABLISHED,同时客户端协议栈也会对服务端的 SYN 包进行应答,应答数据为 server_isn+1;

ACK 应答包到达服务端后,服务端的 TCP 连接进入 ESTABLISHED 状态,同时服务端协议栈使得 accept 阻塞调用返回,这个时候服务端到客户端的单向连接也建立成功,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 TCP 全连接队列,等待进程调用 accept 函数时把连接取出来。至此,客户端与服务端两个方向的连接都建立成功。


从上面的描述过程,我们可以得知客户端 connect 成功返回是发送完第三次握手后,服务端 accept 成功返回是在三次握手成功之后。所以,服务端调用listen之后sleep休眠,并不会影响 TCP 三次握手的过程,当服务端调用listen后,操作系统内核会开始监听指定端口上的连接请求。即使服务端进入sleep休眠状态,内核仍然会继续监听连接请求,并将其放入到TCP全连接队列中,等待服务端调用 accpet 来获取连接。

问题二.如何保证数据双写一致?
在分布式系统中,数据库和缓存会搭配一起使用,以此来保证程序的整体查询性能。也就说,分布式系统为了缓解数据库查询的压力,会将查出来的数据保存在缓存中,下次再查询时,直接走缓存系统,而不再查询数据库,这样就极大的提高了整体的查询性能。

缓存之所以比数据库快的主要原因有以下:
内存访问速度快:缓存通常将数据存储在内存中,而数据库将数据存储在磁盘上。相比于磁盘访问,内存访问速度更快,可以达到纳秒级别的读取速度,远远快于数据库的毫秒级别的读取速度。
IO 操作次数少:数据库通常需要进行磁盘 IO 操作,包括读取和写入磁盘数据。而缓存将数据存储在内存中,避免了磁盘 IO 的开销。内存访问不需要进行磁盘寻址和机械运动,相对来说速度更快。

特殊的数据结构:缓存的数据结构通常为 key-value 形式的,也就是说缓存可以做到任何数据量级下的查询数据复杂度为 O(1),所以它的查询效率是非常高的;而数据库采用的是传统数据结构设计,可能需要查询二叉树、或全文搜索、或回表查询等操作,所以其查询性能是远低于缓存系统的。


解决缓存和数据库一致问题的常见解决方案有以下 4 种:
1.先修改数据库,后更新缓存。
2.先更新缓存,后修改数据库。
3.先修改数据库,后删除缓存。
4.先删除缓存,后修改数据库。

问题三.Redis持久化策略有哪些?
Redis 4.0 之后支持以下 3 种持久化方案:
RDB(Redis DataBase)持久化:快照方式持久化,将某一个时刻的内存数据,以二进制的方式写入磁盘;
AOF(Append Only File)持久化:文件追加持久化,记录所有非查询操作命令,并以文本的形式追加到文件中;

混合持久化:RDB + AOF 混合方式的持久化,Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能减低数据丢失的风险。


RDB 持久化机制有以下优缺点:
优点:
速度快:相对于 AOF 持久化方式,RDB 持久化速度更快,因为它只需要在指定的时间间隔内将数据从内存中写入到磁盘上。
空间占用小:RDB 持久化会将数据保存在一个压缩的二进制文件中,因此相对于 AOF 持久化方式,它占用的磁盘空间更小。
恢复速度快:因为 RDB 文件是一个完整的数据库快照,所以在 Redis 重启后,可以非常快速地将数据恢复到内存中。
缺点:
数据可能会丢失:RDB 持久化方式只能保证数据在指定时间间隔内写入磁盘,因此如果 Redis 进程崩溃或者服务器断电,从最后一次快照保存到崩溃的时间点之间的数据可能会丢失。

实时性差:因为 RDB 持久化是定期执行的,因此从最后一次快照保存到当前时间点之间的数据可能会丢失。如果需要更高的实时性,可以使用 AOF 持久化方式。


AOF 持久化机制有以下优缺点:
优点:
数据不容易丢失:AOF 持久化方式会将 Redis 执行的每一个写命令记录到一个文件中,因此即使 Redis 进程崩溃或者服务器断电,也可以通过重放 AOF 文件中的命令来恢复数据。
实时性好:由于 AOF 持久化方式是将每一个写命令记录到文件中,因此它的实时性比 RDB 持久化方式更好。
数据可读性强:AOF 持久化文件是一个纯文本文件,可以被人类读取和理解。
缺点:
写入性能略低:由于 AOF 持久化方式需要将每一个写命令记录到文件中,因此相对于 RDB 持久化方式,它的写入性能略低。
占用磁盘空间大:由于 AOF 持久化方式需要记录每一个写命令,因此相对于 RDB 持久化方式,它占用的磁盘空间更大。

AOF 文件可能会出现损坏:由于 AOF 文件是不断地追加写入的,因此如果文件损坏,可能会导致数据无法恢复。


问题四.什么是多态?
多态是面向对象编程中的一个重要概念,它允许通过父类类型的引用变量来引用子类对象,并在运行时根据实际对象的类型来确定调用哪个方法。换句话说,一个对象可以根据不同的情况表现出多种形态。

多态有以下几个特点和优势:
可替换性:子类对象可以随时替代父类对象,向上转型。
可扩展性:通过添加新的子类,可以扩展系统的功能。
接口统一性:可以通过父类类型的引用访问子类对象的方法,统一对象的接口。

代码的灵活性和可维护性:通过多态,可以将代码编写成通用的、松耦合的形式,提高代码的可维护性。


实现原理:
动态绑定(Dynamic Binding):指的是在编译时,Java 编译器只能知道变量的声明类型,而无法确定其实际的对象类型。而在运行时,Java 虚拟机(JVM)会通过动态绑定来解析实际对象的类型。这意味着,编译器会推迟方法的绑定(即方法的具体调用)到运行时。正是这种动态绑定机制,使得多态成为可能。

虚拟方法调用(Virtual Method Invocation):在 Java 中,所有的非私有、非静态和非 final 方法都是被隐式地指定为虚拟方法。虚拟方法调用是在运行时根据实际对象的类型来确定要调用的方法的机制。当通过父类类型的引用变量调用被子类重写的方法时,虚拟机会根据实际对象的类型来确定要调用的方法版本,而不是根据引用变量的声明类型。


问题五.如果不使用双亲委派会有什么问题?
双亲委派模型指的是当一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最 终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无 法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

启动类加载器:加载 JDK 中 lib 目录中 Java 的核心类库,即$JAVA_HOME/lib目录。扩展类加载器。加载 lib/ext 目录下的类;
应用程序类加载器:加载我们写的应用程序;

自定义类加载器:根据自己的需求定制类加载器。


如果不使用双亲委派模型,可能存在以下问题:
安全性问题:双亲委派模型可以通过从上层类加载器向下层加载器委派类加载请求来提高安全性。如果没有双亲委派机制,那些由上层类加载器加载的核心类可能会被替换成恶意代码,从而导致安全漏洞。
资源浪费问题:没有双亲委派机制,每个类加载器都有自己的类加载搜索路径和加载规则。这可能导致同一个类被不同的类加载器重复加载,造成资源的浪费。
类冲突问题:在没有双亲委派机制的情况下,不同的类加载器可以独立加载相同的类。这可能导致类的冲突和不一致性,因为同一个类在不同的类加载器中会有多个版本存在,最终导致类的不一致问题。
双亲委派模型是保证 Java 应用程序的稳定性和安全性的重要机制,使用双亲委派模型能够避免类的冲突、提高安全性、节省资源,并保证类的一致性。
用户评论