java面试

java基础面试题

java基础类型

总共有8种,分别是

byte、bool、char、short、int、long、float、double

面向过程是什么

面向过程是分析解决问题的步骤,然后用函数把这些步骤一步一步实现,然后再使用的过程中一一调用即可,性能较高

面向对象是什么

把构成问题的事务分解成各个对象,而建立对象的目的不是为了完成一个一个步骤,而是为了描述某个事物再解决某个问题中发生的行为,所以衍生出面向对象的三个特性,封装、继承、多态,所以易维护、易复用、易扩展。容易设计出低耦合的系统,但是他的性能肯定比面向对象要低。

重写和重载的区别

重写

主要是子类重写父类的方法,方法名、返回值、方法参数必须相同,访问修饰符一定要大于重写的访问修饰符

重载

相同的方法名,不是的参数是重载,返回类型可相同也可以不相同

什么是内部类

在java中,可以将一个类定义在另一个类中,分为成员内部类,匿名内部类、局部内部类、静态内部类

image-20240221200821355

HashCode的作用

java集合有两类,list和set,前者可以重复,后者不可以重复,那么怎么去判断是否重复,就需要使用equals方法,但是每次都使用这个方法,效率比较低,于是我们可以把对象计算成,一个哈希码,方便分组存储

mysql面试

预读取

当server来查数据库的时候,会有个预读取,就是从mysql多读取一点数据,加载到缓存区,一次读取4k,读取4次,总共读取16k。为什么要这么做呢?

是为了加速数据的读取

buffer pool

上面说到了mysql会有预读取,那么读取到哪呢?缓存区!这个缓存区就是buffer pool,默认是128m,这个可以自行设置缓存区大小,上面的预读取大小是不能设置的,除非重新编译mysql源码。

那么当buffer pool设置的内存满了怎么办,会有一个淘汰策略:LRU,淘汰冷的数据,mysql的LRU是一个链表,有8分之5的热区,8分之3的冷区,当数据第一次记录到buffer pool那么他会把数据放在冷区的头部,当1000毫秒类这个数据被再次访问,就会把它放到热区。

redo log

什么是redo log,这个是记录buffer pool得日志,因为buffer pool是一个内存数据,当电脑断电之后内存数据会丢失,所以会有个redo log日志来记录buffer pool的数据。

undo log

撤销日志!记录事务操作前的状态,当事务操作失败之后,会从这个日志回滚数据。

更新操作流程

  1. 从存储引擎,拿到数据,记录到buffer pool,返回给server层
  2. server拿到数据后,对数据进行修改
  3. 调用存储引擎,记录到buffer pool
  4. Redo Log 、Undo Log 记录
  5. 事务提交

image-20240226171249550

Mysql为什么使用B+数做为索引

  1. B+树能显著减少IO次数,提高效率
  2. B+得查询效率更稳定,因为数据都在叶子节点
  3. B+那能提高范围查询的效率,因为叶子节点是双向链接的
  4. B+采取顺序读取

线程池

线程池的参数

核心线程数

线程池最终保留的线程数

最大线程数

线程池允许的最大线程数,当工作队列满了之后,可以创建的最大线程数

线程存活保留时间

线程结束任务之后保留的时间

存活时间单位

当线程池的当前线程,大于核心线程,那么线程的空闲时间,大于设置存活时间会被销毁

工作队列

线程池核心线程数量满了,来的任务存入工作队列中排队等待

线程池工厂

用于创建新的线程

拒绝策略

核心线程数+任务队列满了,来的任务做的策略

设置线程池参数

核心线程数

这个是要分业务类型的,分以下几种类型:

  • CPU密集型
  • IO密集型
  • 混合类型(CPU密集和IO密集都有)

什么是CPU密集呢?涉及到计算或者数据转换等等,不涉及到阻塞得情况,就是CPU密集。那么如何设置核心线程数呢,可以设置CPU核心数+-1。


什么是IO密集呢?涉及到查询数据、对接第三方接口的这种IO操作,涉及到阻塞就是IO密集型,这个如何设置呢?貌似没有公式


什么是混合类型呢?就是CPU密集和IO密集都有,这个好像也没有公式


其实最终线程池的核心线程数的设置,是要经过压测的,举个例子,我要设置CPU密集的线程池,那么我的核心线程数设置为CPU核心+1,然后进行压测,然后动态的调整核心线程数量,慢慢的压测看处理时间,最终选择一个适合的数量。

总结就是先估一个值,在压测,最终选择一个适合的数量。

最大线程数

理论上设置和核心线程数一样的,就是核心线程数=最大线程数。为什么呢?因为我们的核心线程数理论上设置了最优的线程数量,没必要在多设置工作线程了。

工作队列

这个是要结合业务去设置,要预估我处理一个工作线程要多长时间,在预估可能会有几个工作的数量,或者这样思路,我这个任务可以容忍最大的时间处理。

拒绝策略

总共有四种拒绝策略

  • 直接抛异常
  • 不处理
  • 让主线程去执行,就是工作队列满了之后,到拒绝策略的方法了,直接去把任务执行掉
  • 删除掉旧任务,插入新的工作,因为工作队列是一个链表,当核心线程满了之后,就往工作队列插入任务(Runable),每次都是往后插,当工作队列满了之后,就把前面的删除掉,继续尝试插入工作队列

线程池的工作流程

  1. 判断核心线程是否空闲,如果空闲执行任务
  2. 如果核心线程满了,插入工作队列
  3. 工作队列满了之后,判断当前线程池的工作线程,是否大于设置的线程池最大线程,如果不大于,新创建线程执行任务
  4. 如果线程池的工作线程大于最大线程,那就执行拒绝策略

拒绝策略

拒绝策略分4种

  • 直接抛异常
    • 工作队列满了,直接抛一个异常,这个工作也不要了
  • 不处理
    • 什么都不干,这个工作不要了
  • 让主线程去执行,就是工作队列满了之后,到拒绝策略的方法了,直接去把任务执行掉
    • 工作队列满了,走到拒绝策略,那我直接把任务给干了,不用线程池的线程了。
  • 删除掉旧任务,插入新的工作,因为工作队列是一个链表,当核心线程满了之后,就往工作队列插入任务(Runable),每次都是往后插,当工作队列满了之后,就把前面的删除掉,继续尝试插入工作队列
    • 这个就是删除老了插入新的任务

拒绝策略根据具体的类型去选择,当我的任务是记录日志这些东西,我认为这些日志可能不重要那就直接扔了。

当我的任务很重要的时候,那我可以把这些任务给序列化,或者丢给MQ去执行,就是留痕,实在不行让主线程去执行