第一轮:Java基础与多线程
面试官:请介绍一下Java线程池的核心参数。
谢飞机:呃……核心参数?是不是就是new ThreadPoolExecutor()里的那几个?我大概记得有核心线程数、最大线程数,还有一个队列,叫什么来着……LinkedBlockingQueue?
面试官:还行,至少记得名字。那这些参数分别起什么作用?
谢飞机:核心线程数就是一直待命的工人,最大线程数是忙的时候最多能叫多少人,队列就是任务排着等,如果队列满了……就……新开线程?直到最大?
面试官:勉强正确。那如果使用Executors.newFixedThreadPool(),底层用的是什么队列?
谢飞机:哦!这个我知道!是LinkedBlockingQueue,而且是无界的!
面试官:很好,那这种无界队列在高并发下可能带来什么问题?
谢飞机:嗯……内存会爆?OutOfMemoryError?
面试官:不错,有风险意识。
第二轮:JUC与并发编程
面试官:说说ConcurrentHashMap在Java 8中是如何保证线程安全的?
谢飞机:哦!它不是用synchronized锁整个表,而是……分段锁?不对,那是Java 7。Java 8是用CAS + synchronized,只锁链表或红黑树的头节点!
面试官:很好。那如果一个key的hash冲突特别多,性能会怎样?
谢飞机:链表变长,查找慢……哦对,Java 8会转成红黑树,O(n)变成O(log n)!
面试官:不错。那CopyOnWriteArrayList适合什么场景?
谢飞机:读多写少!每次写都复制新数组,读不用加锁,很适合配置监听之类的。
面试官:理解到位。
第三轮:Spring生态与分布式
面试官:Spring Bean的作用域有哪些?
谢飞机:singleton、prototype,还有request、session……吧?
面试官:那singleton一定是线程安全的吗?
谢飞机:呃……Bean本身是单例,但如果它有成员变量,多个线程改的话……就不安全了!
面试官:正确。那Dubbo的负载均衡策略有哪些?
谢飞机:随机、轮询、最少活跃数……我记得最少活跃数是选处理得最快的服务器。
面试官:不错。最后一个问题:Redis的持久化机制?
谢飞机:RDB是快照,AOF是日志追加……RDB恢复快但可能丢数据,AOF更安全但文件大。
面试官:可以。今天的面试就到这里,你先回去等通知吧。
谢飞机:好嘞!谢谢面试官!
答案详解
1. Java线程池核心参数
- corePoolSize:核心线程数,即使空闲也不会被回收(除非设置allowCoreThreadTimeOut)。
- maximumPoolSize:最大线程数,超过core后创建的线程数上限。
- workQueue:任务等待队列,常用
LinkedBlockingQueue(无界)、ArrayBlockingQueue(有界)。 - keepAliveTime:非核心线程空闲存活时间。
- handler:拒绝策略,如AbortPolicy、CallerRunsPolicy等。
使用
newFixedThreadPool时,workQueue为LinkedBlockingQueue(Integer.MAX_VALUE),极易导致OOM。
2. ConcurrentHashMap(Java 8)
- 采用“CAS + synchronized”代替分段锁。
- 数组元素(Node)的插入使用CAS,冲突时使用
synchronized锁住当前桶(链表头或红黑树根)。 - 当链表长度 ≥ 8 且数组长度 ≥ 64 时,链表转红黑树,提升查找效率。
3. CopyOnWriteArrayList
- 写操作复制新数组,完成后原子替换,读操作无锁。
- 适用于读远多于写的场景,如事件监听器列表。
- 缺点:写开销大,数据一致性为最终一致。
4. Spring Bean作用域
- singleton:容器中唯一实例,注意非线程安全。
- prototype:每次获取都创建新实例。
- request/session/application:Web相关作用域。
5. Dubbo负载均衡
- Random:按权重随机,默认。
- RoundRobin:轮询。
- LeastActive:优先调用活跃数最少的提供者,体现响应速度。
- ConsistentHash:相同参数请求始终落在同一节点,用于缓存场景。
6. Redis持久化
- RDB:定时快照,恢复快,数据可能丢失。
- AOF:记录每条写命令,可配置fsync策略(everysec最常用),更安全但体积大。
- 生产建议:同时开启,重启时优先加载AOF。