Android 高级工程师面试参考答案:语言基础与并发

语言基础与并发

这一篇重点解决一个问题:为什么面试官明明在招 Android,却总是先问 Java / Kotlin / 并发

原因很简单。高级工程师不是只会写页面,而是要能处理线程切换、状态一致性、性能瓶颈和复杂异步流程。语言基础不扎实,后面的优化和架构很容易停留在口号层面。

1. HashMap 为什么线程不安全?ConcurrentHashMap 为什么更适合并发?

参考答案

HashMap 的底层是数组加链表加红黑树。它在并发写入时线程不安全,核心原因不是"它没加锁"这么简单,而是多个线程可能同时触发扩容、链表插入或元素覆盖,导致数据丢失、覆盖甚至结构异常。

ConcurrentHashMap 的设计目标是让读尽可能无锁、写尽量降低锁粒度。JDK 1.8 以后它主要通过 CAS + synchronized 控制桶级别的并发更新,而不是像早期版本那样使用大段锁。这样能在保证线程安全的同时保留较高吞吐。

面试官继续追问什么

  • 为什么 HashMap 扩容时更容易暴露并发问题?
  • ConcurrentHashMap 为什么不允许 keyvaluenull
  • synchronized 放在桶节点上,为什么比整张表加锁更高效?

追问怎么答

  • HashMap 扩容时会重算桶位置并迁移节点,多线程同时迁移更容易出现覆盖、丢失和结构异常,所以并发问题在扩容阶段更容易暴露。
  • ConcurrentHashMap 不允许 null,是为了避免并发读场景下分不清"这个 key 不存在"还是"这个 key 存的值就是 null"。
  • 桶级别加锁只阻塞冲突桶的写入,其他桶仍可并发更新,所以吞吐明显好于整张表一把大锁。

项目中怎么回答

如果你的业务里有内存缓存、请求去重表、下载任务表、页面状态表,尽量结合它们举例。高级岗位更想听到你是否知道"哪里该用并发容器,哪里不该共享可变状态"。

直接套用句式

"我们项目里有一类共享状态,比如请求去重表/下载任务表/内存缓存,这类场景我会优先考虑线程安全容器;但如果状态只在单线程闭环里消费,我不会为了看起来并发安全就把所有东西都换成重型并发结构。"

2. volatile 能解决什么问题?为什么不能保证原子性?

参考答案

volatile 主要保证两件事:可见性和有序性。一个线程修改了变量,其他线程能更快看到最新值;同时编译器和 CPU 不会随意把它前后的指令重排到破坏语义的程度。

volatile 不能保证复合操作的原子性,比如 count++ 本质上是读取、加一、写回三步。多个线程同时执行时,即使每一步都可见,最终结果仍然可能丢失更新。

面试官继续追问什么

  • 为什么双重检查单例要配合 volatile
  • 什么场景下 volatile 比加锁更合适?
  • volatileAtomicInteger 的边界区别是什么?

追问怎么答

  • 双重检查单例配合 volatile,是为了禁止"对象先赋值、后初始化完成"的指令重排,避免别的线程拿到半初始化对象。
  • volatile 更适合状态通知类场景,比如停止标记、配置开关、是否已初始化这类只要求可见性的变量。
  • AtomicInteger 适合需要原子更新的计数和累加,volatile 只能保证看见最新值,不能保证 ++ 这种复合操作安全。

项目中怎么回答

可以讲页面状态位、任务取消标记、单例初始化等场景。重点不是背概念,而是能说明什么时候只需要"状态通知",什么时候必须要"互斥修改"。

直接套用句式

"像页面销毁标记、停止开关、是否已初始化这种状态,我更关注的是其他线程能不能及时看到,所以 volatile 往往够用;但如果是计数、累加、复合写入,我就不会指望 volatile,而是会换成原子类或锁。"

3. synchronizedReentrantLock 怎么选?

参考答案

synchronized 语法简单、可读性好,由 JVM 保证加锁和释放,适合大多数互斥场景。ReentrantLock 更灵活,支持可中断锁、超时尝试、公平锁以及多个条件队列,在复杂同步控制里更有优势。

如果只是保护一个简单临界区,优先 synchronized。如果你需要可中断等待、精细化唤醒或者更复杂的线程协作,就考虑 ReentrantLock

面试官继续追问什么

  • 什么叫可重入?
  • 公平锁和非公平锁的区别与代价是什么?
  • 为什么高级岗位不建议为了"更高级"就默认用 ReentrantLock

追问怎么答

  • 可重入就是同一个线程拿到某把锁后,可以再次进入同一把锁保护的代码,而不会把自己锁死。
  • 公平锁按等待顺序分配,更"公平",但吞吐通常更差;非公平锁允许插队,性能往往更高,所以默认更常用。
  • ReentrantLock 不是默认更高级,只是更灵活。大部分简单互斥场景用 synchronized 可读性更好、出错面更小。

项目中怎么回答

你可以结合磁盘缓存写入、数据库串行访问、任务队列调度这些实际问题来讲。高级面试更看你有没有"为场景选工具",而不是机械对比 API。

直接套用句式

"我一般不会先比 API,而是先看场景。如果只是简单互斥,我优先 synchronized;如果要可中断等待、超时控制或者更复杂的线程协作,我才会考虑 ReentrantLock。"

4. 线程池有哪些关键参数?为什么很多线上问题都和线程池配置有关?

参考答案

线程池最关键的几个参数是:

  • corePoolSize:核心线程数,通常长期保留。
  • maximumPoolSize:最大线程数。
  • workQueue:任务队列。
  • keepAliveTime:非核心线程空闲多久回收。
  • RejectedExecutionHandler:满载后的拒绝策略。

线上问题常出在两个地方。第一,线程数开太大,导致 CPU 争抢、上下文切换频繁;第二,队列开太大,导致任务堆积、超时、内存上涨,但表面上看不到明显报错。

面试官继续追问什么

  • CPU 密集和 IO 密集线程池为什么配置不同?
  • 为什么"线程越多越快"是错的?
  • 你如何判断当前线程池是线程数问题还是队列问题?

追问怎么答

  • CPU 密集任务主要受核心数限制,线程太多只会增加切换成本;IO 密集任务大量时间在等待,所以可以适当放更多线程提高利用率。
  • 线程越多不代表越快,因为线程会争抢 CPU、内存和锁,过多时上下文切换本身就会变成开销。
  • 看任务执行耗时、排队时长、拒绝次数和线程活跃数。如果线程长期满但队列短,多半是线程数或任务耗时问题;如果队列持续堆积,更像消费速度跟不上。

项目中怎么回答

最好能讲一个真实例子,比如图片预加载、日志落盘、离线同步、首页并发任务编排。说明你怎么通过监控、埋点、耗时分布和拒绝次数去判断问题。

直接套用句式

"我在线程池问题上不太会只背参数,而是会结合真实链路看任务耗时、排队时长和活跃线程数。因为很多线上问题不是线程池不能用,而是线程数和队列配置跟业务类型不匹配。"

5. 协程为什么适合 Android?它和线程到底是什么关系?

参考答案

协程不是线程,它更像一种轻量级的异步任务抽象。线程是操作系统调度单位,协程由用户态调度器管理。协程的优势在于:写法接近同步代码、切换成本低、取消和作用域控制更自然,特别适合 Android 页面生命周期和异步链路。

但协程不是免费午餐。它只是在合适的挂起点让出执行权,不会自动让阻塞代码变成非阻塞。如果你在协程里直接做阻塞 IO 或长时间计算,问题一样会发生。

面试官继续追问什么

  • suspend 函数为什么不能直接在普通函数里调用?
  • 协程挂起时到底保存了什么?
  • 为什么协程也会导致内存泄漏或任务泄漏?

追问怎么答

  • suspend 函数依赖协程上下文和调度器,普通函数没有这套运行时支撑,所以不能直接调用。
  • 协程挂起时本质上会保存当前执行状态、局部变量和后续恢复点,恢复后能从上次停下的位置继续跑。
  • 如果协程作用域绑错、持有页面引用、页面销毁后任务不取消,协程一样会把对象和任务生命周期拖长,形成泄漏或"幽灵任务"。

项目中怎么回答

可以用页面数据加载、搜索联想、上传下载、重试控制这些例子。高级岗位更想知道你是否把协程当成了"结构化并发工具",而不是"新语法糖"。

直接套用句式

"我在项目里用协程,主要不是为了写法新,而是为了把任务归属、取消和异常收口做得更清楚。尤其在页面生命周期相关的请求和状态收集上,它比手写回调链更稳。"

6. launchasyncwithContext 的区别是什么?

参考答案

  • launch 用于启动一个不直接返回结果的协程,返回 Job
  • async 用于并发执行并返回结果,返回 Deferred,通常要配合 await()
  • withContext 更像"切换上下文并等待结果返回",常用于明确地把任务切到 IODefault 线程池执行。

面试时最好补一句:async 不是越多越好,如果任务之间并没有并发收益,只会增加调度复杂度和异常处理成本。

面试官继续追问什么

  • async 的异常什么时候抛出?
  • 为什么 withContext 常比嵌套 launch 更容易维护?
  • 页面销毁时,哪些协程应该取消?

追问怎么答

  • async 的异常通常在 await() 时暴露,如果根本不 await,就容易把异常和任务结果都"藏起来"。
  • withContext 会切线程并等待结果返回,流程是串起来的;嵌套 launch 容易把控制流拆散,异常和取消也更难收口。
  • 和页面生命周期绑定的请求、轮询、收集任务都应该在页面销毁或离开可见状态时取消,避免回调到失效 UI。

7. 协程取消是怎么传播的?为什么有时你调用 cancel() 却没停下来?

参考答案

协程取消依赖协作式取消。也就是说,任务需要在挂起点、检查点或者显式检查取消状态时才能响应。像 delay()withContext()、大多数挂起函数都能感知取消;但如果你在协程里跑一个长时间的普通循环或阻塞调用,不主动检查 isActive,它就不会及时停下。

结构化并发的价值就在这里:父协程取消时,子协程默认一起取消,生命周期更容易统一管理。

面试官继续追问什么

  • SupervisorJob 为什么不会像普通父子关系那样传播失败?
  • CancellationException 为什么通常不当成业务异常处理?
  • 你如何处理"页面退出但网络请求仍然回调 UI"的问题?

追问怎么答

  • SupervisorJob 的设计目标就是隔离失败,一个子任务出错不应把兄弟任务全带崩,更适合页面里多个相对独立的异步任务。
  • CancellationException 在协程里更多代表正常取消信号,不是业务失败;如果把它当错误上报,噪音会很多。
  • 关键是把请求放到正确作用域里,让页面退出自动取消;同时 UI 更新前再检查生命周期状态,避免失效回调。

8. FlowStateFlowSharedFlow 怎么区分?

参考答案

Flow 默认是冷流,只有收集时才开始执行,适合一次性异步数据链路和流式转换。StateFlow 是热流,始终持有一个最新状态,适合页面状态建模。SharedFlow 也是热流,但更适合事件广播或多订阅分发,因为它不强制要求永远有一个当前值。

在现代 Android 架构里,一个很常见的组合是:

  • StateFlow 表示页面状态
  • SharedFlow 表示一次性事件
  • 普通 Flow 表示数据处理管道

面试官继续追问什么

  • 为什么"一次性事件"不适合直接用 StateFlow
  • Flow 的背压问题怎么理解?
  • repeatOnLifecycle 解决了什么问题?

追问怎么答

  • StateFlow 会一直保留最新值,页面重建或重新订阅时可能把"跳转、Toast"这类一次性事件再发一次。
  • 背压可以理解为上游产出太快、下游处理太慢,结果就是堆积、延迟甚至资源浪费;Flow 相关操作符就是在帮助你控制这件事。
  • repeatOnLifecycle 会在生命周期进入目标状态时开始收集、离开时自动取消,避免页面不可见时还在白白收流。

收尾建议

这一篇的重点不是把所有并发原语都背下来,而是建立一个判断框架:

  • 这个问题是可见性问题,还是互斥问题?
  • 是 CPU 瓶颈、IO 瓶颈,还是调度问题?
  • 是该共享状态,还是该避免共享?
  • 是该并发执行,还是该串行收敛?

把这些问题答清楚,你的回答就会明显区别于只会背八股的候选人。

相关推荐

《Java Synchronized 和 ReentrantLock》
《Java 线程池深入理解》

系列导航

下一篇:《Android 高级工程师面试参考答案:Framework、生命周期、View 与 Binder》

相关推荐
therese_100862 小时前
安卓-CeilingNestedScrollView
android
memcpy02 小时前
LeetCode 1202. 交换字符串中的元素【无向图连通分量】中等
算法·leetcode·职场和发展
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题】【Java基础篇】第5题:HashMap的底层原理是什么
java·开发语言·数据结构·后端·面试·hash-index·hash
凤年徐2 小时前
自动化构建工具:make 与 Makefile
android·java·linux·自动化
三少爷的鞋2 小时前
从 Callback 到 Coroutines:Android 异步并发方案的演进
android
鹏程十八少2 小时前
4. 2026金三银四 Android OkHttp 面试核心 45 问:从源码到架构深度解析
android·前端·面试
90后的晨仔11 小时前
Android Studio 项目模板完全指南
android
summerkissyou198711 小时前
Android-SurfaceView-投屏-常见问题
android·surfaceview
明天就是Friday11 小时前
Android实战项目④ OkHttp WebSocket开发即时通讯App 完整源码详解
android·websocket·okhttp