JAVA 面试题 合辑(三)(未完待续)

JAVA 面试题 合辑(一)https://blog.csdn.net/haponchang/article/details/92741553

JAVA 面试题 合辑(二)https://blog.csdn.net/haponchang/article/details/92829739

JAVA 面试题 合辑(三)https://blog.csdn.net/haponchang/article/details/92833016

目录

框架源码解读

tomcat的架构

Mybatis架构

hibernate架构,hibernate二级缓存如何实现

MQ详解

设计模式

单例模式(线程安全且内存消耗小)

Java中如何实现代理机制(JDK、CGLIB)

分布式

RPC原理,使用过的框架

分布式场景的一些问题,分布式事务,分段提交实现,分布式锁如何实现

zookeeper组件,介绍选主算法

分布式集群下如何做到唯一序列号

设计一个秒杀系统,30 分钟没付款就自动关闭交易。

如何使用 redis 和 zookeeper 实现分布式锁?有什么区别优缺点,分别适用什么场景

如果有人恶意创建非法连接,怎么解决

分布式事务的原理,优缺点,如何使用分布式事务。

什么是一致性 hash。

什么是 restful,讲讲你理解的 restful。

解释什么是 MESI 协议(缓存一致性)

说说你知道的几种 HASH 算法,简单的也可以。

什么是 paxos 算法

什么是 zab 协议

一个在线文档系统,文档可以被编辑,如何防止多人同时对同一份文档进行编辑更新

Dubbo Cluster 实现集群

线上系统突然变得异常缓慢,你如何查找问题

一次 RPC 请求的流程是什么

异步模式的用途和意义

编程中自己都怎么考虑一些设计原则的,比如开闭原则,以及在工作中的应用

后台系统怎么防止请求重复提交

描述一个服务从发布到被消费的详细过程

讲讲你理解的服务治理。

如何做到接口的幂等性

架构

微服务架构

算法


框架源码解读

tomcat的架构

处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。加载和管理 Servlet,以及具体处理 Request 请求。因此 Tomcat 设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。

tomcat如何加载servlet

当客户请求某个资源时,HTTP 服务器会用一个 ServletRequest 对象把客户的请求信息封装起来,然后调用 Servlet 容器的 service 方法,Servlet 容器拿到请求后,根据请求的 URL 和 Servlet 的映射关系,找到相应的 Servlet,如果 Servlet 还没有被加载,就用反射机制创建这个 Servlet,并调用 Servlet 的 init 方法来完成初始化,接着调用 Servlet 的 service 方法来处理请求,把 ServletResponse 对象返回给 HTTP 服务器,HTTP 服务器会把响应发送给客户端。

Mybatis架构

待定

hibernate架构,hibernate二级缓存如何实现

待定

JDBC相关知识,比如PrepareStatementStatement的优缺点,优化 

待定

MQ详解

当客消息中间件的组成

1 Broker

消息服务器,作为server提供消息核心服务

 2 Producer

消息生产者,业务的发起方,负责生产消息传输给broker,

 3 Consumer

消息消费者,业务的处理方,负责从broker获取消息并进行业务逻辑处理

4 Topic

主题,发布订阅模式下的消息统一汇集地,不同生产者向topic发送消息,由MQ服务器分发到不同的订阅者,实现消息的       广播

 5 Queue

队列,PTP模式下,特定生产者向特定queue发送消息,消费者订阅特定的queue完成指定消息的接收

 6 Message

消息体,根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输

消息中间件模式分类

1 点对点

PTP点对点:使用queue作为通信载体

说明: 
消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。 
消息被消费以后,queue中不再存储,所以消息消费者不可能消费到已经被消费的消息。 Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。

2 发布/订阅

Pub/Sub发布订阅(广播):使用topic作为通信载体 

消息中间件的优势

1 系统解耦

交互系统之间没有直接的调用关系,只是通过消息传输,故系统侵入性不强,耦合度低。

 2 提高系统响应时间

例如原来的一套逻辑,完成支付可能涉及先修改订单状态、计算会员积分、通知物流配送几个逻辑才能完成;通过MQ架构设计,就可将紧急重要(需要立刻响应)的业务放到该调用方法中,响应要求不高的使用消息队列,放到MQ队列中,供消费者处理。

 3 为大数据处理架构提供服务

通过消息作为整合,大数据的背景下,消息队列还与实时处理架构整合,为数据处理提供性能支持。

  4 Java消息服务——JMS

Java消息服务(Java Message Service,JMS)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。 
JMS中的P2P和Pub/Sub消息模式:点对点(point to point, queue)与发布订阅(publish/subscribe,topic)最初是由JMS定义的。这两种模式主要区别或解决的问题就是发送到队列的消息能否重复消费(多订阅)。

设计模式

单例模式(线程安全且内存消耗小)

单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点!

懒汉,线程不安全

public class Singleton {

    private static Singleton instance;

    private Singleton (){}

    public static synchronized Singleton getInstance() {

if (instance == null) {

    instance = new Singleton();

}

return instance;

    }

}
 

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。

饿汉

public class Singleton {  

    private static Singleton instance = new Singleton();  

    private Singleton (){}  

    public static Singleton getInstance() {  

    return instance;

    }

}

        这种方式基于classloder机制避免了多线程的同步问题,instance在类装载时就实例化。目前java单例是指一个虚拟机的范围,因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的ClassLoader装载饿汉式实现单例类的时候就会创建一个类的实例。这就意味着一个虚拟机里面有很多ClassLoader,而这些classloader都能装载某个类的话,就算这个类是单例,也能产生很多实例。当然如果一台机器上有很多虚拟机,那么每个虚拟机中都有至少一个这个类的实例的话,那这样 就更不会是单例了。(这里讨论的单例不适合集群!)

静态内部类

public class Singleton {  

    private static class SingletonHolder {  

    private static final Singleton INSTANCE = new Singleton();

    }  

    private Singleton (){}  

    public static final Singleton getInstance() { 

    return SingletonHolder.INSTANCE;  

    }  

      这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载!这个时候,这种方式相比第2种方式就显得很合理。

枚举 

public enum Singleton {

    INSTANCE;

    public void whateverMethod() {

    }

}

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

双重校验锁(jdk1.5)

public class Singleton {

    private volatile static Singleton singleton;

    private Singleton (){}

    public static Singleton getSingleton() {

if (singleton == null) {

    synchronized (Singleton.class) {

if (singleton == null) {

    singleton = new Singleton();

}

    }

}

return singleton;

    }

}

这样方式实现线程安全地创建实例,而又不会对性能造成太大影响。它只是第一次创建实例的时候同步,以后就不需要同步了。

由于volatile关键字屏蔽了虚拟机中一些必要的代码优化,所以运行效率并不是很高,因此建议没有特别的需要不要使用。双重检验锁方式的单例不建议大量使用,根据情况决定。

Java中如何实现代理机制(JDKCGLIB)

JDK动态代理:代理类和目标类实现了共同的接口,用到InvocationHandler接口。
CGLIB动态代理:代理类是目标类的子类,用到MethodInterceptor接口。

分布式

RPC原理,使用过的框架

待定

分布式场景的一些问题,分布式事务,分段提交实现,分布式锁如何实现

待定

zookeeper组件,介绍选主算法

待定

分布式集群下如何做到唯一序列号

Redis生成,mongodb的objectId,zk生成

设计一个秒杀系统,30 分钟没付款就自动关闭交易。

分流 – 限流–异步–公平性(只能参加一次)–用户体验(第几位,多少分钟,一抢完)
容错处理
Redis 队列 mysql

30分钟关闭 可以借助redis的发布订阅机制 在失效时进行后续操作,其他mq也可以

如何使用 redis zookeeper 实现分布式锁?有什么区别优缺点,分别适用什么场景

首先分布式锁实现常见的有数据库锁(表记录),缓存锁,基于zk(临时有序节点可以实现的)的三种

Redis适用于对性能要求特别高的场景。redis可以每秒执行10w次,内网延迟不超过1ms

缺点是数据存放于内存,宕机后锁丢失。

锁无法释放?使用Zookeeper可以有效的解决锁无法释放的问题,因为在创建锁的时候,客户端会在ZK中创建一个临时节点,一旦客户端获取到锁之后突然挂掉(Session连接断开),那么这个临时节点就会自动删除掉。其他客户端就可以再次获得锁。

非阻塞锁?使用Zookeeper可以实现阻塞的锁,客户端可以通过在ZK中创建顺序节点,并且在节点上绑定监听器,一旦节点有变化,Zookeeper会通知客户端,客户端可以检查自己创建的节点是不是当前所有节点中序号最小的,如果是,那么自己就获取到锁,便可以执行业务逻辑了。

不可重入?使用Zookeeper也可以有效的解决不可重入的问题,客户端在创建节点的时候,把当前客户端的主机信息和线程信息直接写入到节点中,下次想要获取锁的时候和当前最小的节点中的数据比对一下就可以了。如果和自己的信息一样,那么自己直接获取到锁,如果不一样就再创建一个临时的顺序节点,参与排队。

单点问题?使用Zookeeper可以有效的解决单点问题,ZK是集群部署的,只要集群中有半数以上的机器存活,就可以对外提供服务。

如果有人恶意创建非法连接,怎么解决

可以使用filter过滤处理

分布式事务的原理,优缺点,如何使用分布式事务。

Two Phase commit协议

优点是可以管理多机事务,拥有无线扩展性 确定是易用性难,承担延时风险

JTA,atomiks等

什么是一致性 hash

一致性hash是一种分布式hash实现算法。满足平衡性 单调性 分散性 和负载。

什么是 restful,讲讲你理解的 restful

REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。

解释什么是 MESI 协议(缓存一致性)

MESI是四种缓存段状态的首字母缩写,任何多核系统中的缓存段都处于这四种状态之一。我将以相反的顺序逐个讲解,因为这个顺序更合理:

失效(Invalid)缓存段,要么已经不在缓存中,要么它的内容已经过时。为了达到缓存的目的,这种状态的段将会被忽略。一旦缓存段被标记为失效,那效果就等同于它从来没被加载到缓存中。

共享(Shared)缓存段,它是和主内存内容保持一致的一份拷贝,在这种状态下的缓存段只能被读取,不能被写入。多组缓存可以同时拥有针对同一内存地址的共享缓存段,这就是名称的由来。

独占(Exclusive)缓存段,和S状态一样,也是和主内存内容保持一致的一份拷贝。区别在于,如果一个处理器持有了某个E状态的缓存段,那其他处理器就不能同时持有它,所以叫“独占”。这意味着,如果其他处理器原本也持有同一缓存段,那么它会马上变成“失效”状态。

已修改(Modified)缓存段,属于脏段,它们已经被所属的处理器修改了。如果一个段处于已修改状态,那么它在其他处理器缓存中的拷贝马上会变成失效状态,这个规律和E状态一样。此外,已修改缓存段如果被丢弃或标记为失效,那么先要把它的内容回写到内存中——这和回写模式下常规的脏段处理方式一样。

说说你知道的几种 HASH 算法,简单的也可以。

哈希(Hash)算法,即散列函数。 它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程。 同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出

MD4 MD5 SHA

什么是 paxos 算法

Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的"La",此人现在在微软研究院)于1990年提出的一种基于消息传递的一致性算法。

什么是 zab 协议

ZAB 是 Zookeeper 原子广播协议的简称

整个ZAB协议主要包括消息广播和崩溃恢复两个过程,进一步可以分为三个阶段,分别是:

发现 Discovery

同步 Synchronization

广播 Broadcast

组成ZAB协议的每一个分布式进程,都会循环执行这三个阶段,将这样一个循环称为一个主进程周期。

一个在线文档系统,文档可以被编辑,如何防止多人同时对同一份文档进行编辑更新

点击编辑的时候,利用redis进行加锁setNX完了之后 expire 一下。

也可以用版本号进行控制

Dubbo 的原理,数据怎么流转的,怎么实现集群,负载均衡,服务注册和发现。重试转发,快速失败的策略是怎样的。

Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。

Dubbo Cluster 实现集群

在集群负载均衡时,Dubbo提供了多种均衡策略,缺省为random随机调用。

Random LoadBalance:随机,按权重比率设置随机概率。

RoundRobin LoadBalance:轮循,按公约后的权重比率设置轮循比率。

LeastActive LoadBalance:最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

ConsistentHash LoadBalance:一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

快速失败,只发起一次调用,失败立即报错。

线上系统突然变得异常缓慢,你如何查找问题

逐级排查(网络,磁盘,内存,cpu),数据库,日志,中间件等也可通过监控工具排查

一次 RPC 请求的流程是什么

1)服务消费方(client)调用以本地调用方式调用服务;

2)client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;

3)client stub找到服务地址,并将消息发送到服务端;

4)server stub收到消息后进行解码;

5)server stub根据解码结果调用本地的服务;

6)本地服务执行并将结果返回给server stub;

7)server stub将返回结果打包成消息并发送至消费方;

8)client stub接收到消息,并进行解码;

9)服务消费方得到最终结果。

异步模式的用途和意义

异步模式使用与服务器多核,并发严重的场景

可提高服务吞吐量大,不容易受到冲击,可以采用并发策略,提高响应时间

缓存数据过期后的更新如何设计。

失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。

命中:应用程序从cache中取数据,取到后返回。

更新:先把数据存到数据库中,成功后,再让缓存失效。

编程中自己都怎么考虑一些设计原则的,比如开闭原则,以及在工作中的应用

开闭原则(Open Close Principle)

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

里氏代换原则(Liskov Substitution Principle)

子类型必须能够替换掉它们的父类型。

依赖倒转原则(Dependence Inversion Principle)

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程

接口隔离原则(Interface Segregation Principle)

建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少

组合/聚合复用原则

说要尽量的使用合成和聚合,而不是继承关系达到复用的目的

迪米特法则(Law Of Demeter)

迪米特法则其根本思想,是强调了类之间的松耦合,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成影响,也就是说,信息的隐藏促进了软件的复用。

单一职责原则(Single Responsibility Principle)

一个类只负责一项职责,应该仅有一个引起它变化的原因

后台系统怎么防止请求重复提交

可以通过token值进行防止重复提交,存放到redis中,在表单初始化的时候隐藏在表单中,添加的时候在移除。判断这个状态即可防止重复提交。

如何看待缓存的使用(本地缓存,集中式缓存),简述本地缓存和集中式缓存和优缺点。本地缓存在并发使用时的注意事项。

描述一个服务从发布到被消费的详细过程

讲讲你理解的服务治理。

如何做到接口的幂等性

设计一个社交网站中的私信功能,要求高并发、可扩展等等。 画一下架构图。

应用服务器怎么监控性能,各种方式的区别

如何设计一套高并发支付方案,架构如何设计

如何实现负载均衡,有哪些算法可以实现

请思考一个方案,设计一个可以控制缓存总体大小的自动适应的本地缓存

请思考一个方案,实现分布式环境下的 countDownLatch

架构

微服务架构

微服务架构下,服务调用主要依赖下面几个基本组件:

服务描述

服务调用首先要解决的问题就是服务如何对外描述。比如服务名、调用这个服务需要提供哪些信息、返回的结果是什么格式的、如何解析等问题。

常用的服务描述方式包括 RESTful API、XML 配置以及 IDL 文件三种。

其中,RESTful API 方式通常用于 HTTP 协议的服务描述,并且常用 Wiki 或者Swagger来进行管理。下面是一个 RESTful API 方式的服务描述的例子。

XML 配置方式多用作 RPC 协议的服务描述,通过 *.xml 配置文件来定义接口名、参数以及返回值类型等。

如motan_server.xml中

<motan:basicService id="serviceBasicConfig" export="demoMotan:8002" group="motan-demo-rpc" module="motan-demo-rpc" registry="registry"/>

IDL 文件方式通常用作 Thrift 和 gRPC 这类跨语言服务调用框架中,比如 gRPC 就是通过 Protobuf 文件来定义服务的接口名、参数以及返回值的数据结构。

注册中心

有了服务的接口描述,下一步要解决的问题就是服务的发布和订阅,就是说你提供了一个服务,如何让外部想调用你的服务的人知道。这个时候就需要一个类似注册中心的角色,服务提供者将自己提供的服务以及地址登记到注册中心,服务消费者则从注册中心查询所需要调用的服务的地址,然后发起请求。

一般来讲,注册中心的工作流程是:

服务提供者在启动时,根据服务发布文件中配置的发布信息向注册中心注册自己的服务。

服务消费者在启动时,根据消费者配置文件中配置的服务信息向注册中心订阅自己所需要的服务。

注册中心返回服务提供者地址列表给服务消费者。

当服务提供者发生变化,比如有节点新增或者销毁,注册中心将变更通知给服务消费者。

服务框架

通过注册中心,服务消费者就可以获取到服务提供者的地址,有了地址后就可以发起调用。但在发起调用之前你还需要解决以下几个问题。

服务通信采用什么协议?就是说服务提供者和服务消费者之间以什么样的协议进行网络通信,是采用四层 TCP、UDP 协议,还是采用七层 HTTP 协议,还是采用其他协议?

数据传输采用什么方式?就是说服务提供者和服务消费者之间的数据传输采用哪种方式,是同步还是异步,是在单连接上传输,还是多路复用。

数据压缩采用什么格式?通常数据传输都会对数据进行压缩,来减少网络传输的数据量,从而减少带宽消耗和网络传输时间,比如常见的 JSON 序列化、Java 对象序列化以及 Protobuf 序列化等。

服务监控

一旦服务消费者与服务提供者之间能够正常发起服务调用,你就需要对调用情况进行监控,以了解服务是否正常。通常来讲,服务监控主要包括三个流程。

指标收集。就是要把每一次服务调用的请求耗时以及成功与否收集起来,并上传到集中的数据处理中心。

数据处理。有了每次调用的请求耗时以及成功与否等信息,就可以计算每秒服务请求量、平均耗时以及成功率等指标。

数据展示。数据收集起来,经过处理之后,还需要以友好的方式对外展示,才能发挥价值。通常都是将数据展示在 Dashboard 面板上,并且每隔 10s 等间隔自动刷新,用作业务监控和报警等。

服务追踪

除了需要对服务调用情况进行监控之外,你还需要记录服务调用经过的每一层链路,以便进行问题追踪和故障定位。

服务追踪的工作原理大致如下:

服务消费者发起调用前,会在本地按照一定的规则生成一个 requestid,发起调用时,将 requestid 当作请求参数的一部分,传递给服务提供者。

服务提供者接收到请求后,记录下这次请求的 requestid,然后处理请求。如果服务提供者继续请求其他服务,会在本地再生成一个自己的 requestid,然后把这两个 requestid 都当作请求参数继续往下传递。

以此类推,通过这种层层往下传递的方式,一次请求,无论最后依赖多少次服务调用、经过多少服务节点,都可以通过最开始生成的 requestid 串联所有节点,从而达到服务追踪的目的。

服务治理

服务监控能够发现问题,服务追踪能够定位问题所在,而解决问题就得靠服务治理了。服务治理就是通过一系列的手段来保证在各种意外情况下,服务调用仍然能够正常进行。

项目的优化,怎么提高app编译速度,

将模块解藕,拆分成单个module

待定

怎样提升APP的响应速度

待定

设计一个秒杀系统

待定

分布式场景的一些问题,分布式事务,分段提交实现,分布式锁如何实现

待定

架构搭建的体会

算法

链表反转

二叉树前中后序查找

非波拉契数列

一个很大的数组,找出前K大个数,复杂度

奇数打印一个链表的值

最大公共子序列

两条相交的单向链表,如何求他们的第一个公共节点

格雷编码,

分割回文串的问题

单链表中倒数第k个节点的算法

二分法排序算法

快排的基本思想及时间复杂度

10 亿个数字里里面找最小的 10 个。

有 1 亿个数字,其中有 2 个是重复的,快速找到它,时间和空间要最优。

2 亿个随机生成的无序整数,找出中间大小的值。

给一个不知道长度的(可能很大)输入字符串,设计一种方案,将重复的字符排重。

遍历二叉树。

有 3n+1 个数字,其中 3n 个中是重复的,只有 1 个是不重复的,怎么找出来。

写一个字符串反转函数。

常用的排序算法,快排,归并、冒泡。 快排的最优时间复杂度,最差复杂度。冒泡排序的优化方案。

二分查找的时间复杂度,优势。

一个已经构建好的 TreeSet,怎么完成倒排序。

LRU(近期最少使用算法)

TTL(超时算法) 去除ttl最大的键值

http://wiki.jikexueyuan.com/project/redis/data-elimination-mechanism.html

http://www.infoq.com/cn/articles/tq-redis-memory-usage-optimization-storage

http://www.redis.cn/topics/cluster-tutorial.html

猜你喜欢

转载自blog.csdn.net/haponchang/article/details/92833016