腾讯一面
腾讯一面完整面试复盘(25题)。覆盖多线程编程、Java并发、集合源码、Spring事务、MySQL隔离级别、分布式事务、Redis实战、项目经验。每道题附带详细解析。
一、多线程编程题
Q1-3:交替打印线程 + 信号量 + 可重入锁
题目:两个线程交替打印字符串。面试者用两个Semaphore解决,面试官追问:两字符串长度不一致怎么办?为什么不用ReentrantLock?
Semaphore方案
ReentrantLock为什么不是首选?
- Semaphore天然适合"许可"模型(线程1许可=可打印,线程2许可=可打印)
- ReentrantLock配合Condition也能实现,但需要两个Condition + 一个状态变量,代码更复杂
- 可重入锁(ReentrantLock)的含义:同一线程可以多次获取同一把锁而不会死锁。synchronized也是可重入的。用于递归调用或嵌套调用同一把锁的场景
Q4-5:多线程扩展 + 原子类
追问:如果有多个线程呢?Java原子类有哪些?
- 多线程扩展:N个线程交替打印 → 用N个Semaphore循环释放,或使用AtomicInteger标记当前轮到谁;策略模式优化:将"轮到谁"的判断逻辑抽象为策略接口,不同规则实现不同策略
- 原子类:AtomicInteger/Long/Boolean(基本类型)、AtomicReference(引用)、AtomicStampedReference(带版本号解决ABA)、LongAdder(高并发累加优于AtomicLong)
二、HashMap与ConcurrentHashMap
Q7-8:底层原理 + 时间复杂度
关键纠正:面试者说HashMap操作都是O(1),面试官反问"确定吗?"——HashMap在hash碰撞严重时退化到O(n)(链表)或O(log n)(红黑树)。
|
操作 |
平均 |
最坏 |
|---|---|---|
|
get/put/remove |
O(1) |
O(n)链表 / O(log n)红黑树 |
ConcurrentHashMap核心要点
- JDK7:分段锁(Segment extends ReentrantLock),16段,并发度16
- JDK8:CAS + synchronized锁桶头节点,锁粒度更细。put时桶空CAS写入,桶非空synchronized锁头节点。get无锁(volatile保证可见性)
- 不允许null键值:防止并发环境下的二义性(get返回null无法区分"不存在"和"值为null")
三、线程池与并发基础
Q9-10:线程创建方式 + 拒绝策略
线程创建4种方式:继承Thread、实现Runnable、实现Callable+FutureTask、线程池(ExecutorService)。
4种拒绝策略:AbortPolicy(抛异常,默认)、CallerRunsPolicy(调用者线程执行)、DiscardPolicy(直接丢弃)、DiscardOldestPolicy(丢弃队首)。
Q11:BIO vs NIO
|
BIO |
NIO |
|
|---|---|---|
|
模型 |
阻塞IO,一个连接一个线程 |
非阻塞IO,一个线程管理多个连接(Selector) |
|
核心 |
ServerSocket + Socket |
Channel + Buffer + Selector |
|
场景 |
连接数少且固定的架构 |
高并发、连接数多但数据量短的场景 |
四、Spring核心
Q12-13:Bean生命周期 + 事务
Bean生命周期:实例化 → 属性注入 → Aware回调 → BeanPostProcessor前置 → @PostConstruct → InitializingBean → BeanPostProcessor后置 → 就绪 → @PreDestroy → 销毁
事务传播(7种):REQUIRED(默认,加入或新建)、REQUIRES_NEW(挂起当前新建)、NESTED(savepoint嵌套)、SUPPORTS/NOT_SUPPORTED/NEVER/MANDATORY
Q15-16:事务失效场景
同类方法调用事务失效:this.methodB()不经过代理对象,事务不生效。解决:注入自身 → 调用代理对象的方法;或AopContext.currentProxy();或将方法拆分到不同Service。
异步方法事务:@Async + @Transactional不能直接用在一个方法上,因为异步是新线程,事务不会传播到新线程。
五、MySQL事务
Q14:四种隔离级别
|
级别 |
解决的问题 |
存在的问题 |
|---|---|---|
|
READ UNCOMMITTED |
— |
脏读、不可重复读、幻读 |
|
READ COMMITTED |
脏读 |
不可重复读、幻读 |
|
REPEATABLE READ(MySQL默认) |
脏读、不可重复读 |
幻读(MVCC+间隙锁基本解决) |
|
SERIALIZABLE |
全部 |
性能极低 |
六、分布式事务
Q17:微服务间如何实现分布式事务?
核心方案:
- Seata(AT/TCC/Saga模式):AT自动回滚、TCC手动confirm/cancel、Saga长事务补偿
- 本地消息表 + MQ:本地事务中写消息表 → 定时任务扫描未发送消息 → MQ投递 → 消费端幂等处理
- RocketMQ事务消息:半消息→本地事务→commit/rollback
- 最终一致性 > 强一致性:分布式系统中优先保证最终一致性,配合补偿机制和监控告警
七、项目深挖(海报渲染 + 排行榜 + 消息推送)
Q19-21:海报渲染超时兜底
- 重试+MQ:超时后放入MQ延迟队列,消费者重试渲染
- 一直超时的降级:返回默认海报模板(静态图)、降级为纯文字卡片、异步通知用户稍后查看
- 业务层面:预生成常用海报模板并缓存、限制海报复杂度(最大图层数/最大渲染时长)
Q22-25:Redis Zset排行榜 + 消息推送
- Zset key/value:key=step_rank:{date}:{slot},member=user:{id},score=复合分数(分数×1e13+时间编码)
- 防重复发送:消息表+唯一索引、Redis SETNX做幂等、消息ID去重
- Zset内存优化:ziplist编码(小数据)、数据TTL过期、冷热分离(Redis热+ES/Hive冷)、Redis Cluster水平分片
- Redis挂了怎么办:Redis Cluster自动故障转移(Sentinel)、本地缓存兜底、MQ消息持久化保证不丢、RDB+AOF持久化
面试总结:腾讯一面的广度较大(多线程→集合→Spring→MySQL→分布式→项目),核心考察实战经验和技术深度。HashMap复杂度纠正、事务失效场景、分布式事务方案、Redis降级方案是区分度较高的点。