面试官:Java线程池的核心参数了解吗?谢飞机:会用new Thread就不错了!

41 阅读5分钟

面试官:Java线程池的核心参数了解吗?谢飞机:会用new Thread就不错了!

面试官:请坐,先简单自我介绍一下。

谢飞机:面试官好,我叫谢飞机,三年Java开发经验,精通CRUD,熟悉复制粘贴,会用new Thread创建线程,曾独立完成登录注册功能!

面试官:……好的。那我们开始吧。

第一轮:基础热身(3个问题)

面试官:你说你会用 new Thread,那如果系统有大量并发请求,你还用这种方式吗?

谢飞机:啊?不是,我可以用多个 new Thread 啊,多建几个不就行了?

面试官:……这会导致线程频繁创建销毁,资源耗尽。你应该使用线程池。

谢飞机:哦!线程池我知道!就是 ThreadPoolExecutor 对吧?我在网上抄过代码!

面试官:嗯,那它的核心参数有哪些?

谢飞机:这个……corePoolSize、maximumPoolSize、workQueue、keepAliveTime、threadFactory、handler —— 六大参数我都背下来了!

面试官:不错。那如果任务提交进来,线程池是怎么处理的?

谢飞机:emmm……先塞队列?队列满了再开新线程?再不行就拒绝?好像和银行叫号差不多?

面试官:比喻得意外地准确。加分。

第二轮:深入源码与实战(4个问题)

面试官:ConcurrentHashMap 为什么是线程安全的?

谢飞机:因为加了 synchronized!

面试官:JDK 1.8 呢?

谢飞机:呃……也加了 synchronized 吧……

面试官:错了。1.8 使用的是什么?

谢飞机:是不是 ReentrantLock?

面试官:接近了,是 synchronized + CAS + volatile。具体说说?

谢飞机:额……CAS 是比较并交换,volatile 是保证可见性……synchronized 是锁方法……然后……然后它们一起努力,就安全了!

面试官:……你这叫“玄学编程”。

面试官:Spring 中 Bean 的作用域有哪些?

谢飞机:Singleton 和 Prototype!我常用单例!

面试官:还有呢?

谢飞机:Request、Session……还有 Application?不对,那是 Servlet 的。哦对,还有 Request、Session、GlobalSession!

面试官:很好。那 Spring Boot 自动装配原理?

谢飞机:靠 @SpringBootApplication 注解!里面有个 @EnableAutoConfiguration!它会扫描 META-INF/spring.factories 文件,把里面的配置类加载进来!

面试官:不错,理解到位。

第三轮:高并发与分布式(5个问题)

面试官:Redis 如何实现分布式锁?

谢飞机:用 SET key value NX EX!原子操作上锁!

面试官:如果业务执行时间超过锁过期时间怎么办?

谢飞机:那就……再续一小时?

面试官:怎么续?

谢飞机:启动一个定时任务,每隔一段时间给锁延长有效期?

面试官:这个机制叫什么?

谢飞机:叫……“狗剩续命”?

面试官:……叫看门狗机制(Watchdog)。

面试官:RabbitMQ 消息丢失怎么办?

谢飞机:开启持久化!消息发完后确认!消费者手动 ACK!

面试官:xxl-job 如何保证任务不重复执行?

谢飞机:额……靠数据库锁?

面试官:Dubbo 负载均衡策略有哪些?

谢飞机:Random、RoundRobin……还有 Weight?

面试官:最后一个,DDD 中的聚合根是什么?

谢飞机:就是……一个很厉害的根?管理一堆实体?

面试官:……行吧。

面试官:今天先到这里,你的表现……很有特色。回去等通知吧。

谢飞机:好的!谢谢面试官!我回去就把 new Thread 全换成线程池!


答案详解

1. ThreadPoolExecutor 核心参数与执行流程

  • corePoolSize:核心线程数,即使空闲也不会被回收(除非设置 allowCoreThreadTimeOut)。
  • maximumPoolSize:最大线程数。
  • workQueue:阻塞队列,如 LinkedBlockingQueue、ArrayBlockingQueue。
  • keepAliveTime:非核心线程空闲存活时间。
  • threadFactory:创建线程的工厂。
  • handler:拒绝策略,如 AbortPolicy、CallerRunsPolicy。

执行流程

  1. 线程数 < corePoolSize:直接创建新线程执行任务。
  2. 线程数 ≥ corePoolSize:任务进入 workQueue。
  3. workQueue 满且线程数 < maximumPoolSize:创建非核心线程执行任务。
  4. 线程数 = maximumPoolSize 且 workQueue 满:执行拒绝策略。

2. ConcurrentHashMap 线程安全原理(JDK 1.8)

  • 使用 synchronized 锁住链表或红黑树的头节点,粒度更小。
  • 使用 CAS 操作进行 put 操作的初始化和扩容标记。
  • 使用 volatile 保证变量的可见性,如 sizeCtl、modCount。
  • 相比 JDK 1.7 的分段锁(Segment),1.8 实现更轻量、高效。

3. Spring Bean 作用域

  • singleton:默认,容器中唯一实例。
  • prototype:每次获取都创建新实例。
  • request:每个 HTTP 请求创建一个实例。
  • session:每个 HTTP Session 创建一个实例。
  • application:每个 ServletContext 共享一个实例。
  • websocket:每个 WebSocket 会话一个实例。

4. Spring Boot 自动装配原理

  • @EnableAutoConfiguration 通过 @Import 导入 AutoConfigurationImportSelector。
  • 该组件读取 classpath 下所有 jar 包中的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件(旧版为 spring.factories)。
  • 根据条件注解(@ConditionalOnClass、@ConditionalOnMissingBean 等)决定是否加载某个自动配置类。

5. Redis 分布式锁

  • 使用 SET key value NX EX timeout 命令实现原子性上锁。
  • value 通常为唯一标识(如 UUID),防止误删。
  • 释放锁时需确保是自己的锁,使用 Lua 脚本保证原子性删除。
  • 看门狗机制:Redisson 客户端在锁快过期时自动延长有效期,避免业务未执行完锁就失效。

6. RabbitMQ 消息可靠性

  • 生产者到 Broker:开启 confirm 模式,异步接收 Broker 确认。
  • Broker 持久化:交换机、队列、消息都设置持久化(durable=true)。
  • 消费者消费:关闭 autoAck,手动 ACK,防止消费失败消息丢失。

7. xxl-job 任务去重

  • 通过数据库加悲观锁(SELECT FOR UPDATE)或乐观锁(version 字段)来保证同一任务在同一时间只被一台执行器执行。

8. Dubbo 负载均衡策略

  • RandomLoadBalance:随机,权重越大越容易被选中(默认)。
  • LeastActiveLoadBalance:最少活跃调用数,相同则随机。
  • ConsistentHashLoadBalance:一致性哈希,相同参数请求总落到同一提供者。
  • RoundRobinLoadBalance:轮询。

9. DDD 聚合根(Aggregate Root)

  • 聚合是一组相关对象的集合,由一个根实体(聚合根)统一对外暴露。
  • 所有外部对象只能通过聚合根访问其内部实体或值对象。
  • 聚合根负责维护聚合内部的一致性,保证业务规则。
  • 数据库事务边界通常以聚合为单位。