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

32 阅读4分钟

第一轮:线程与线程池的初探

面试官:请介绍一下Java中创建线程的几种方式?

谢飞机:我知道!有继承Thread类、实现Runnable接口,还有Callable+Future,对吧?我还知道lambda可以简化写法。

面试官(点头):不错,基础扎实。那我们深入一点——线程池你用过吗?

谢飞机:当然!我天天用new Thread(),比线程池快多了,还不占内存!

面试官(皱眉):……那你听说过Executors工具类吗?

谢飞机:听说过!它能帮我们execute()线程,就像执行命令一样!

面试官:那你用过ThreadPoolExecutor吗?它的核心参数有哪些?

谢飞机:嗯……corePoolSize、maximumPoolSize、keepAliveTime、workQueue,还有……还有一个叫什么来着?哦对,threadFactory和handler!我背过八股文!

面试官(略感欣慰):还行。那如果核心线程数满了,新任务会怎么处理?

谢飞机:直接开新线程,直到最大线程数,再不行就丢掉任务!简单粗暴,高效!


第二轮:并发容器与Spring Bean

面试官:既然提到并发,ConcurrentHashMapHashMap有什么区别?

谢飞机HashMap不是线程安全的,ConcurrentHashMap是线程安全的!我项目里都用后者,稳如老狗!

面试官:JDK1.8之后它是怎么保证线程安全的?

谢飞机:加了synchronized锁整个table?不对……好像是每个链表头加锁?哦!是CAS + synchronized!

面试官:很好。那Spring中Bean的作用域有哪些?

谢飞机:singleton、prototype,还有request、session……我一般只用单例。

面试官:如果一个prototype的Bean注入到一个singleton的Bean中,会出现什么问题?

谢飞机:呃……内存泄漏?不不不,应该是每次都能拿到新的prototype实例!因为Spring很聪明!

面试官(扶额):实际上默认情况下,只会注入一次。要解决这个问题,可以用@Lookup或者代理模式。


第三轮:分布式与中间件

面试官:你们项目用Redis做缓存,那缓存穿透怎么解决?

谢飞机:加个if判断,如果为空就不查数据库!完美规避!

面试官:如果恶意请求一直查不存在的key呢?

谢飞机:那就让它穿透呗,反正数据库扛得住!

面试官:……我们通常用布隆过滤器或缓存空值来应对。

面试官:最后一个问题,Dubbo的负载均衡策略有哪些?

谢飞机:随机、轮询、最少活跃调用数,还有……哈希一致性!我全都会!

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

谢飞机:谢谢!我回去就把new Thread删了,改用线程池!


答案详解

1. ThreadPoolExecutor 核心参数

  • corePoolSize:核心线程数,即使空闲也不会被回收(除非设置allowCoreThreadTimeOut)。
  • maximumPoolSize:最大线程数,线程池允许创建的最大线程数量。
  • keepAliveTime:非核心线程空闲时的存活时间。
  • workQueue:任务队列,如ArrayBlockingQueue、LinkedBlockingQueue等。
  • threadFactory:创建线程的工厂,可用于自定义线程名称等。
  • handler:拒绝策略,如AbortPolicy、CallerRunsPolicy等。

任务提交流程:

  1. 当前运行线程 < corePoolSize → 创建新线程执行任务。
  2. ≥ corePoolSize → 添加到队列。
  3. 队列满 → 创建新线程直到 maximumPoolSize。
  4. 达到 maximumPoolSize 且队列满 → 执行拒绝策略。

2. ConcurrentHashMap 实现原理(JDK1.8)

  • 使用 CAS + synchronized 锁住链表头节点或红黑树根节点。
  • 分段锁机制被取消,改为更细粒度的桶级别锁。
  • put操作:计算hash → 找到桶位置 → CAS尝试插入 → 冲突则synchronized同步添加。

3. Spring Bean 作用域

  • singleton:容器中只有一个实例,全局共享。
  • prototype:每次获取都创建新实例。
  • request/session/application:Web环境下对应生命周期。

注意:singleton Bean 注入 prototype Bean 时,默认只注入一次。解决方案:

  • 使用 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 创建代理。
  • 使用 @Lookup 注解方法让容器每次返回新实例。

4. 缓存穿透解决方案

  • 缓存空值:查询结果为空也缓存,设置较短过期时间。
  • 布隆过滤器:在访问缓存前判断key是否存在,避免无效查询击穿数据库。
  • 限流降级:防止恶意攻击导致系统崩溃。

5. Dubbo 负载均衡策略

  • RandomLoadBalance:按权重随机选择(默认)。
  • RoundRobinLoadBalance:轮询。
  • LeastActiveLoadBalance:优先选择活跃调用数最少的提供者。
  • ConsistentHashLoadBalance:相同参数的请求总是发到同一台机器,适用于缓存场景。

本文通过幽默对话形式讲解技术要点,帮助读者轻松掌握Java面试核心知识。