美文网首页
java面试题目

java面试题目

作者: 我不说你不懂_f0c6 | 来源:发表于2021-03-14 15:46 被阅读0次

    1.如何解决hash冲突

    1. 开放地址法

    开放地执法有一个公式:Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1)
    基本思想:当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。

    2. rehash(再hash法)

    使用第二个或第三个...计算地址,知道无冲突。比如:按首字母进行hash冲突了,则按照首字母第二位,进行hash寻址。、

    3. 链地址法(拉链法)

    创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
    java hashmap使用的就是拉链法解决hash碰撞。

    2.HashMap工作原理

    1.在jdk 1.7中,HashMap采用位桶+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。

    2.在jdk 1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

    3. 原理:数组中的每一个元素所在的位置相当于一个位桶,添加元素的时候,首先计算元素key的hash值,确定插入数组中的位置(也就是哪个桶中),如果存在相同的hash值,则放在同一个桶中(元素位置)形成链表,当链表长度超过阈值(8)时,将链表转换为红黑树;

    底层原理:

    HashMap 底层是基于数组和链表实现的,如图所示,其中两个重要的参数:容量和负载因子;容量的默认大小是 16,负载因子是 0.75,当 HashMap 的 size > 16*0.75 时就会发生扩容(容量和负载因子都可以自由调整)

    问题死循环:

    在并发环境下使用 HashMap 容易出现死循环。
    并发场景发生扩容,调用 resize() 方法里的 rehash() 时,容易出现环形链表。这样当获取一个不存在的 key 时,计算出的 index 正好是环形链表的下标时就会出现死循环。

    3. Lucene底层实现原理,它的索引结构

    1. FST(Finite state Transducers) Lucene现在使用的索引结构

    优点:内存占用率低,压缩率一般在3倍~20倍之间、模糊查询支持好、查询快
    缺点:结构复杂、输入要求有序、更新不易
    Lucene里有个FST的实现,从对外接口上看,它跟Map结构很相似,有查找,有迭代:
    可以看出,FST性能基本跟HaspMap差距不大,但FST有个不可比拟的优势就是占用内存小,只有HashMap10分之一左右,这对大数据规模检索是至关重要的,毕竟速度再快放不进内存也是没用的。
     Lucene现在采用的数据结构为FST,它的特点就是:
      1、词查找复杂度为O(len(str))
      2、共享前缀、节省空间
      3、内存存放前缀索引、磁盘存放后缀词块
      这跟我们前面说到的词典结构三要素是一致的:1. 查询速度。2. 内存占用。3. 内存+磁盘结合。我们往索引库里插入四个单词abd、abe、acf、acg,看看它的索引文件内容。

    4.Spring 启动过程

    Spring启动过程是IOC容器的启动过程,本质是创建和初始化bean工厂(BeanFactory).BeanFactory是Spring IOC的核心,Spring使用beanFactory来实例化,配置和管理bean。
    对于web程序,IOC容器启动过程即是建立上下文的过程,web容器会提供一个全局的servletContext上下文环境。
    其启动过程主要包含三个类,ContextLoaderListener,ContextLoader和XmlWebApplicationContext。
    在web.xml中提供ContextLoaderListener上下文监听器,在web容器启动时,会触发容器初始化事件,ContextLoaderListener会监听到这个事件,从而触发ContextInitialized方法完成上下文初始化,这个方法中调用父类ContextLoader的方法完成上下文初始化。ContextLoader类中主要完成三件事:1)创建WebApplicationContext;2)加载对应的Spring配置文件中的bean;(refresh方法,完成bean的加载)3)将WebApplicationContext放入servletContext中。

    ContextLoaderListener监听器初始化完之后,开始初始化web.xml中配置的servlet,如DispatcherSevlet
    ContextLoaderListener监听器监听的是servletContext,当web容器初始化后,servletContext发生变化时,会触发相应事件。

    Spring bean的生命周期

    new->属性注入->Init生命周期初始化方法->初始化回调方法->代理AOP->放入单例池 singletonObject

    5.redis知识点

    3缓存雪崩

    当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力,造成数据库后端故障,从而引起应用服务器雪崩。

    解决方案:

    1、避免缓存集中失效,不同的key设置不同的超时时间
    2、增加互斥锁,控制数据库请求,重建缓存。
    3、提高缓存的HA,如:redis集群。

    • 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

    • 缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

    • 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

    三、缓存穿透解决方案

    一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
    有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

    四、缓存击穿解决方案

    key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。
    使用互斥锁(mutex key)
    业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
    SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。

    AOF与RDB

    RDB 的优缺点
    优点:
    1 适合大规模的数据恢复。
    2 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
    缺点:
    1 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
    2 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。
    所以Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。

    AOF 的优缺点

    优点:数据的完整性和一致性更高
    缺点:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。

    6.Mysql 索引覆盖

    一颗索引树就完成查询,避免回表查询。

    7.Mysql查询突然某一页卡死

    先是我最开始的sql语句

    select * from table limit 100000,20
    执行时间3.26秒

    然后优化limit

    优化后的sql语句

    select * from table where id > (select id from table limit 100000,1) limit 100000,20
    运行一下,时间0.11秒

    这个提升速度,惊呆了,是不是!

    嗯,问题虽然是解决了,但是里面的原理呢?

    继续深究一下

    引用一下大神的文章:传送门

    来自雅虎的几位工程师带来了一篇”EfficientPagination Using MySQL”的报告
    Limit 10000,20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行,问题就在这里。
    LIMIT 451350 , 30 扫描了45万多行,怪不得慢的都堵死了。
    但是
    limit 30 这样的语句仅仅扫描30行

    那就行了,那我们就想办法扫描20行,找到之前的最大记录,然后在这个记录之后去扫描,ok,问题解决了

    嗯,这就是所谓的子查询优化法。需要注意的是:子查询优化法数据必须是连续的,就是说不能有where条件
    其他优化方法:

    倒排表优化法

    反向查找优化法

    limit限制优化法

    只查索引法

    8.Spring Boot自动装配原理

    自动配置原理

    1).SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration

    2).@EnableAutoConfiguration作用:

    • 利用EnableAutoConfigurationImporttSelector给容器中导入一些组件;
    • 可以插件selectImports()方法的内容;
    • List<String> configurations = getCandidateConfigurations(annotationMetadata,arrtibutes)获取候选的配置
    SpringFactoriesLoader.loadFactoryNames()
    扫描所有jar包类路径下,META-INF/spring.factories
    把扫描到的这些文件的内容包装成properties对象
    从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
    每一个这样的AutoConfiguration类都是容器中的一个组件,都加入到容器中,用他们来做配置

    3).每一个自动配置类进行自动配置功能

    5).所有在配置文件中能配置的属性都是在xxxxPropertites类中封装着,配置文件能配置什么就可以参照某个功能对应的这个属性类;

    相关文章

      网友评论

          本文标题:java面试题目

          本文链接:https://www.haomeiwen.com/subject/gzwqcltx.html