2025-02-28-面试总结
磊磊

这两天的面试能约就约,因为马上就是周末了基本上没公司上班,今天约了两场,上午和下午。
我遇到不会的答不上来,或者答出来不是很满意,就很紧张然后磕磕绊绊的,得多面试见识不同的面试官练练胆子,然后看看面试中级程序员主要问哪些问题,


面试过程

上午场的面试:

上午的面试比较无厘头,提的问题我基本能答上来,最后问我还需要了解一些什么?最后就说让我回去等消息,然后软件上说我不通过…问他原因也不说,不过还是总结一下他的问题。

  • 线程的创建方式
  • 线程的几种状态
  • 线程池几种策略
  • 为什么要三次握手
  • Redis 几种类型
  • JVM内存模型
  • JVM如何调优
  • 你还需要了解一些什么?

下午场的面试:

下午的面试体验还可以,但问了几个八股文我答不上来应该是没过,这个公司面试的人比较多,要先填表,HR 还会倒杯水喝,映像很不错比昨天放松一些。我前面那个哥们进去不到 5 分钟就出来了,下一个是我。但面试官看起来比较和善,自我介绍后,问了一些学校、然后工作的项目经历,谈了大概快 20 多分钟整体上还不错挺轻松的,但随之就问八股文了,丫的,问的这些八股文太抽象了,我是一个答不上来,虽然我看到过这种题目但是真没去记这些东西,但是我也说了很多自己的理解,把我知道的都说了,很显然是不标准的,然后开始紧张了…类似于空气凝固了,面试官估计也懂,问我哪时候开始准备面试的,我说昨天才开始面试。

  • SpringBean 生命周期
  • JVM 垃圾回收的几种算法

面试总结

线程的创建方式

我觉得满意的回答(点击展开) 四种:
  • 继承 Thread 类
  • 实现 RunAble 接口
  • 实现 CallAble 接口
  • 线程池

要是只说这四种也太逊了,我觉得得知道哪些时候用哪种,或者说应用场景优劣势。

  • 继承 Thread 类

    • 优点:能直接调用 Thread 类的方法,如 getName,其他实现的接口得用 Thread.currentThread才行
    • 缺点:因为 Java 是单继承多实现的,拓展性差。
  • 实现 RunAble 接口

    • 优点:拓展性高,一个类可以实现多个接口,而且比1方法写法简单,匿名内部类只需写方法的实现即可。
    • 缺点:不能直接调用 Thread 类的方法
  • 使用 Callable 和 Future

    • 优点:可以接受返回值
    • 缺点:相对复杂?性能问题,要创建Future 对象,有一点点开销。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static void extracted() {
// 创建 FutureTask 包装 Callable 任务
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
return null;
}
});
// 创建并启动线程来执行任务
Thread thread = new Thread(futureTask);
thread.start();
try {
// 获取任务执行结果
String result = futureTask.get();
// 打印结果
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
  • Executors 接口实现线程池
    • 优点:高效,面对使用对象简单
    • 缺点:管理和维护较为复杂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args){
/*
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);

参数一:核心线程数量 不能小于0
参数二:最大线程数 不能小于0,最大数量 >= 核心线程数量
参数三:空闲线程最大存活时间 不能小于0
参数四:时间单位 用TimeUnit指定
参数五:任务队列 不能为null
参数六:创建线程工厂 不能为null
参数七:任务的拒绝策略 不能为null
*/
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3, //核心线程数量,能小于0
6, //最大线程数,不能小于0,最大数量 >= 核心线程数量
60,//空闲线程最大存活时间
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(3),//任务队列
Executors.defaultThreadFactory(),//创建线程工厂
new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略
);
}

上午场的面试:

线程的几种状态

我觉得满意的回答(点击展开)
  • 新建 new Thread()
  • 就绪 start() 跟cpu抢夺资源
  • 运行 实际在跑
  • 阻塞 线程被锁
  • 等待 wait(),join(),sleep(),sleep(argument),join(arg),wait(arg)
  • 终止

线程池几种策略

我觉得满意的回答(点击展开)

当线程池和任务队列都满时,线程池会拒绝新任务。常见的拒绝策略包括:

  • AbortPolicy 抛出 RejectedExecutionException 异常,默认策略。
  • CallerRunsPolicy 由调用线程处理该任务。
  • DiscardPolicy 直接丢弃任务,不抛出异常
  • DiscardOldestPolicy 丢弃队列中最旧的任务,并尝试重新提交新任务。

为什么要三次握手

三次握手是为了确保双方都能正常接收和发送数据,防止已失效的连接请求报文段被服务端接收,造成错误。

  1. 第一次握手:客户端发送连接请求报文段,等待服务端确认。
  2. 第二次握手:服务端收到请求后,发送同意连接的确认报文段。
  3. 第三次握手:客户端收到确认后,再次向服务端发送确认报文段,连接建立。

Redis 几种类型

27号有总结

JVM内存模型

比较抽象是以下这几种

  • 堆(Heap)
  • 栈(Stack):
  • 方法区(Method Area)
  • 本地方法栈(Native Method Stack):主要是native,比如 hashcode 这种
  • 程序计数器(Program Counter, PC):存储当前执行指令的地址

JVM如何调优

  • 垃圾回收器(Garbage Collection)
    • 触发GC
  • 内存分配调优、JVM 参数调优
    • 比如IDEA 内存太小,用指令-Xms1024m -Xmx2048m
    • -Xss 设置线程栈大小

你还需要了解一些什么?

这是一道拓展题,一般答得还行几乎都会留给自己发挥,等会总结一下


下午场的面试:

  • SpringBean 生命周期

  • JVM 垃圾回收的几种算法

  1. 标记-清除算法(Mark-Sweep)
    标记阶段:从根节点(如栈和静态变量)开始遍历,对所有可达对象进行标记。
    清除阶段:遍历整个堆,对没有标记的对象进行回收。
    优点:简单、直接。 缺点:内存碎片化严重,影响性能。

  2. 复制算法(Copying)
    复制算法将堆分为两个相等的区域,每次只使用其中一个。当内存用尽时,将存活的对象复制到另一个区域,并清空当前区域。
    优点:高效,避免内存碎片。
    缺点:需要双倍的内存空间。

  3. 标记-整理算法(Mark-Compact)
    这种算法结合了标记-清除和复制算法的优点:
    标记阶段:标记所有可达对象。
    整理阶段:将存活对象移动到堆的一端,按顺序排列,最后清理掉非存活对象。

优点:避免内存碎片。 缺点:对象移动成本高。

  1. 分代收集算法(Generational Collection)
  • 循环依赖怎么解决
  1. 构造器注入:不支持循环依赖。
  2. Setter 注入:Spring 通过三级缓存解决循环依赖。
    • 一级缓存:存放完全初始化好的 Bean。
    • 二级缓存:存放早期暴露的 Bean(未完成初始化)。
    • 三级缓存:存放 Bean 工厂对象,用于解决循环依赖。
由 Hexo 驱动 & 主题 Keep
访客数 访问量