2021年12月1日30道面试题


1.ThreadLocal的作用和原理以及使用场景?

  threadlocal是线程本地变量,它为每一个线程创建一个变量副本,来解决并发冲突问题

  在jdk1.8以之前,每个threadlocal实例维护一个map,每个线程作为key,value保存的就是变量副本

  在jdk1.8,改为每个Thread中维护一个threadlocalMap,threadlocal作为key,value保存变量副本

  这样设计,threadLocalMap中的键值对数量就取决于threadlocal实例的数量,提高了访问效率。当线程被销毁,threadlocalMap也会随之销毁,减少了内存的消耗

  使用场景:

  spring security中,我们使用SecurityContextHolder来获取SecurityContext,

?   比如在springMVC中,我们通过RequestContextHolder来获取当前请求,

?   比如在 zuul中,我们通过ContextHolder来获取当前请求

2.内存溢出和内存泄漏

  内存溢出:没有足够的内存供申请者使用

  内存泄漏:内存中动态分配的内存空间由于某种原因未释放或无法释放,导致内存浪费。比如:使用完的连接没有关闭,使用完的对象没有被回收

3.线程池作用、java中有哪些线程池

  线程池可以控制并发数量,实现线程的复用,也可以管理线程的生命周期

  java中有四种线程池

  1.cachedThreadPool:可缓存的线程池

    corePoolSize的为0,maximumPoolSize为无限大,keepAliveTime为60s,超过这个线程的最大空闲时间,线程就会被销毁,线程最大数量为Integer的最大值

  2.FixedThreadPool:固定线程数量的线程池

    corePoolSize和maximumSize都为用户设置的值,keepAlivetime为0,最大线程数等于核心线程数

  3.SingleThreadPool:单个线程的线程池

    只会创建一个线程来处理任务,串行化执行任务

  4.scheduledThreadPool:可调度的线程池

     最大线程数是Integer的最大值,可以延迟和周期性执行任务

4.线程池的执行流程

  当一个任务来了,如果有空闲的核心线程,直接使用

  如果核心线程都不空闲,就将任务放入队列

  如果队列满了,就会新建非核心线程来处理任务,

  如果新建的非核心线程和核心线程等于最大线程数,就会触发线程池的拒绝策略

5.解释一下线程构造器的7个参数

  CorePoolSize:核心线程数

  MaximunPoolSize:最大线程数:核心线程+非核心线程的,只有当所有核心线程都不空闲且队列也满了,才会创建非核心线程

  KeepAliveTime:非核心线程的最大存活时间,到了这个时间,非核心线程没有被使用,就会被摧毁

  Unit:空闲时间单位,是枚举类型(HOURS,MINUTES)

  WorkQueue:是一个BlockingQueue阻塞队列,超过核心线程的任务会进入队列排队

  ThreadFactory:创建线程的工厂,我们可以自己实现线程的创建

  Handler:饱和处理机制,拒绝策略,当任务数超过最大线程数+队列中任务数,则执行饱和处理机制

6.为什么要使用线程池

  1.可以减少线程的创建和销毁,每个线程可以复用,提高效率

  2.可以根据系统的承受能力,调整线程池中工作的线程数量,减轻服务器压力

7.数据结构有哪些?

  根据逻辑结构分:

    集合:一群没有关系的数据

    线性结构:数据之间存在一对一的关系:如队列,栈、链表

    树形结构:数据之间存在一对多的关系,如二叉树,多叉树

    图形结构:数据之间存在多对多的关系

  根据物理结构分:

    顺序储存结构:用一组连续的内存空间来依次储存线性表的元素,如数组

    链接存储结构:用任意的内存空间来储存线性表中的元素,不要求相邻的元素物理位置也相邻

    索引储存结构:对节点建立索引,通过索引快速找到数据

    散列存储结构:将元素的储存位置和关键字建立联系,又叫hash存储

8.数组和链表在存储结构上有什么区别

  数组的存储需要连续的内存空间,而链表则不需要连续的内存空间,它的存储空间可以是任意位置,因为链表中的每一个元素都保存了相邻元素的指针,但链表容易造成内存碎片化

9.为什么链表容易造成内存碎片化?

  因为链表的元素储存位置在物理位置上不是连续的,它是动态的分配内存空间,不是像数组一样会提前申请一块连续的内存空间

10.什么是散列储存(Hash储存),什么是哈希冲突,怎么解决

  散列储存就是通过将数据的关键字key通过一个函数计算出数据存放的位置,后面要找这个数据,还是通过该函数计算出数据存放的位置来寻找

  哈希冲突就是两个不相同的数据计算出了在同一张hash表中相同的位置(hash值相同,或者下标相同),解决方案可以将两个数据指向同一个链表中,也可以把冲突的hash值再进行计算,直到不冲突为止

  还可以换一种哈希算法重新计算hash值

11.时间复杂度

  时间复杂度是用来衡量算法执行时间的长短

  在哈希表中找一个元素就是O(1)

  访问数组的第n个元素是O(1)

  访问链表的n个元素就是O(n)

  二分搜索的最好情况是O(1),最坏情况是O(lgn)

  一次for循环是O(n)

  两次是O(n2) 三次是O(n3)

12.java中有哪些是线性结构?

  数组、链表、栈(先进后出)(数组栈和链栈)、队列(先进先出)(数组队列和链式队列)、特殊的线性结构String Stringbuffer Stringbuilder

13.树形结构和线性结构的优势?

14.说一下树的分类以及优缺点

  分为二叉树和多叉树

    二叉树的任意节点最多允许有两个子节点,分为:二叉查找树(二叉排序树、二叉搜索树),平衡二叉树、红黑树(自平衡二叉查找树)、赫夫曼树

    多叉树分为:b树、b+树等等

    二分排序树:是有序的二叉树,任意节点左边的子节点小于右边的子节点,二分排序树查找数据就相当于二分查找法,它查找性能很高,但是有可能会倾斜而变成链表结构

    平衡二叉树:左右两颗子树高度的绝对值不超过1,是为了解决二分排序树倾斜变成链表,每次进行添加或者删除操作后,都会进行重排序,因此添加和删除性能较低

    红黑树:是自平衡二叉查找树,它是根据黑色节点的个数和红黑色节点之间的联系来保证树的平衡,红黑树和平衡二叉树在添加和删除操作中,为了保证平衡,都会进行左旋和右旋,不同的是红黑树还会改变节点的颜色,红变黑或者黑变红。在删除和添加操作中,红黑树的旋转次数要少于平衡二叉树,所以一般会优先选用红黑树