携程暑期实习Java一面面经
《面试题目》
- 缓存穿透、雪崩、击穿
- 二级缓存不同节点数据一致性问题?
- ThreadLocal 内存泄露问题
- 子线程如何访问父线程的 ThreadLocal 值
- 线程池工作原理
- 阻塞队列使用 LinkedBlockingQueue 的问题,为什么?OOM 发生在哪个区域?
- 针对线下点餐和线上点餐服务,线程池如何设置参数
- OOM 发生在 JVM 的哪个区域?举例说明
- 项目中如何防止无限的递归调用
- MySQL 索引失效
- HashMap put 过程,扩容原理,为什么在 put 之后检查扩容
- ArrayList 为什么是在添加之前扩容?
- ArrayList 在并发添加元素时会出现什么问题
《参考解析》
- ThreadLocal 内存泄露原理:ThreadLocal 的 Entry 是弱引用指向 ThreadLocalMap 的 key(即 ThreadLocal 实例本身),但 value 是强引用;当 ThreadLocal 实例被回收后,key 变为 null,但对应 value 仍被 Entry 强引用无法回收,如果线程长期存活(如线程池中的线程),就会导致 value 对象持续累积造成内存泄露,因此使用完 ThreadLocal 后应主动调用
remove()。 - 子线程访问父线程 ThreadLocal 值:普通 ThreadLocal 是线程隔离的,子线程无法直接访问父线程设置的值;需要使用
InheritableThreadLocal,它会在子线程创建时把父线程的值拷贝一份到子线程自己的 ThreadLocalMap 中(但线程池复用场景下会失效,需要用阿里开源的TransmittableThreadLocal解决)。 - HashMap vs ArrayList 扩容时机:HashMap 是先插入元素、再判断
size > threshold触发扩容(懒扩容,因为插入前不确定是否会造成哈希冲突超阈值);ArrayList 是先判断容量是否足够、不足则先扩容再插入元素(因为数组必须先有足够空间才能存放新元素,不能像链表那样先挂上去再处理)。 - ArrayList 并发添加问题:ArrayList 不是线程安全的,多线程并发
add()时可能出现:modCount检查失败抛出ConcurrentModificationException、元素覆盖丢失(多个线程同时写入同一size位置)、甚至数组越界异常,解决方案包括使用Collections.synchronizedList()、CopyOnWriteArrayList或加锁。