Android 高级工程师面试:Java 多线程与并发 近1年高频追问 22 题

文章目录

学习建议

目标层级 建议
初级 掌握基础层 8 题:主线程规则、synchronized/volatile 区别、线程状态;工程联想看追问第 1 条
中级 通读全文;进阶层能讲清线程池七大参数、CHM 桶级锁、Handler 泄漏成因与修复
高级 核心层加分项必答:happens-before、AQS、DCL;能串联「同步原语→线程池→协程→泄漏排查」完整链路

基础层(8 题)

#1 线程有哪些创建方式?Android 里怎么选? ⭐

标准回答

常见四种:ThreadRunnableCallable+Future线程池。裸 new Thread 难管控生命周期与数量,工程上应优先 Executor/ThreadPoolExecutor

面试官可能继续追问

  • Android 里线程怎么选?

    网络用 OkHttp 内置 Dispatcher,图片用 Glide 线程池,业务异步优先 Kotlin 协程 + viewModelScope,避免无限创建线程导致 OOM。

  • 为什么不推荐到处 new Thread

    无线程复用、无队列背压,高并发时创建销毁开销大且难统一取消。

  • 协程和线程池是什么关系?

    协程是用户态任务调度,底层常跑在线程池的少量线程上,不是 1:1 替代关系。


#2 线程有哪些状态?如何转换? ⭐

标准回答

六种:NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED。等锁进 BLOCKEDwait()/join()WAITINGsleep()/带超时 waitTIMED_WAITING

面试官可能继续追问

  • Android 里线程状态怎么排查?

    排查 ANR 或线程卡死时,用 Profiler 或 jstack 看主线程是否长期 BLOCKED 在锁或 IO 上。

  • RUNNABLE 一定在 CPU 上跑吗?

    不一定,就绪队列中也算 RUNNABLE,等时间片调度。

  • 主线程 BLOCKED 常见原因?

    同步块里做 IO、等子线程 join、或 Binder 同步调用阻塞。


#3 为什么 Android 主线程不能做耗时操作? 🔥

标准回答

主线程跑 UI 事件循环(Looper 驱动 MessageQueue),耗时任务占住主线程会导致输入、动画、绘制无法及时处理,出现卡顿甚至 ANR。耗时逻辑应丢到 Executor、协程 Dispatchers.IO 或专用 HandlerThread,结果回主线程更新 UI。

面试官可能继续追问

  • ANR 触发阈值是多少?

    输入约 5s、广播 10s、前台 Service 20s(不同场景有差异)。

  • 子线程能直接 update UI 吗?

    不能,必须切回主线程;Compose 也遵循主线程更新状态规则。


#4 synchronized 的原理是什么?锁升级过程? 🔥

标准回答

synchronized 基于监视器锁(Monitor),字节码层有 monitorenter/monitorexit,保证互斥与可见性。JDK 偏向锁→轻量锁→重量锁逐级升级,竞争加剧时膨胀为 OS 互斥量。

面试官可能继续追问

  • Android 里 synchronized 怎么用?

    多线程写共享缓存、单例初始化时常用;注意锁粒度,避免在 UI 路径锁整个大对象。

  • 锁的是对象还是代码?

    锁的是对象监视器;实例方法锁 this,静态方法锁 Class 对象。

  • ReentrantLock 最大区别?

    synchronized JVM 自动释放;ReentrantLock 可中断、可超时、支持公平锁与 Condition


#5 volatile 的语义是什么?能保证原子性吗? 🔥

标准回答

volatile 保证可见性与禁止指令重排,不保证复合操作原子性(如 i++)。适用单一状态标志位,如「请求已取消」「配置已刷新」。

面试官可能继续追问

  • Android 里 volatile 怎么用?

    DCL 单例常配合 volatile 防重排;多字段一致性仍用 synchronized 或原子类。

  • volatilesynchronized 性能差多少?

    无竞争时 volatile 更轻;有互斥需求必须用锁或原子类。

  • DCL 单例为什么需要 volatile

    new 对象指令重排导致其他线程拿到未初始化完成实例。


#6 synchronizedvolatile 怎么选? ⭐

标准回答

要互斥改共享变量用 synchronizedLock/Atomic;只发布单一可见状态用 volatile

面试官可能继续追问

  • Android 里 synchronizedvolatile 怎么选?

    「下载取消标志」可用 volatile boolean;「计数器累加」用 AtomicInteger 或锁,单用 volatile 会丢更新。

  • 多个 volatile 字段能保证组合原子吗?

    不能,读写之间可能被其他线程插入,需整体加锁。

  • Kotlin 里还需要手写 volatile 吗?

    Java 字段可用 @Volatile;多数场景用协程/原子 API 更清晰。


#7 wait/notifysleep 有什么区别? ⭐

标准回答

wait/notify 必须在同步块内调用,释放锁并进入等待队列;notify 唤醒后需重新竞争锁。sleep 不释放锁,仅暂停当前线程指定时间。

面试官可能继续追问

  • Android 里还用 wait/notify 吗?

    业务很少手写,多用 CountDownLatchBlockingQueue 或协程 suspend;但理解有助于读 JDK/框架源码。

  • 为什么 wait 要在 while 循环里?

    防虚假唤醒,被唤醒后应再检查条件。

  • notifynotifyAll 怎么选?

    不确定哪个线程满足条件时用 notifyAll,避免饿死。


#8 什么是线程安全?Android 里如何保证? ⭐

标准回答

多线程并发访问下仍保持正确语义即线程安全。手段:不可变对象、线程封闭(主线程 UI)、同步锁、并发容器、原子类。

面试官可能继续追问

  • Android 里如何保证线程安全?

    RecyclerView 适配器数据源若在后台改、主线程读,需 Copy-on-Write 或主线程统一调度;SparseArray 非线程安全,多线程应加锁或换 ConcurrentHashMap

  • StringBuilder 线程安全吗?

    不安全;多线程拼接用 StringBuffer 或局部 StringBuilder 不外泄。

  • 主线程算线程安全吗?

    单线程内天然安全,但 Handler 延迟任务交叉时仍可能踩共享状态。


进阶层(7 题)

#9 ReentrantLocksynchronized 怎么选? 🔥

标准回答

都能互斥。ReentrantLock 支持可中断、尝试锁、公平锁、Condition 多条件队列,需手动 unlock 且最好在 finally 释放。synchronized 语法简单、JVM 优化成熟。

面试官可能继续追问

  • Android 里 ReentrantLocksynchronized 怎么选?

    简单临界区用 synchronized;需要超时获取或精细唤醒用 ReentrantLock

  • 公平锁会降低吞吐吗?

    会,严格 FIFO 排队减少插队,高竞争下吞吐通常低于非公平锁。

  • 忘记 unlock 会怎样?

    其他线程永久阻塞,类似死锁;必须 try-finally 释放。


#10 ThreadPoolExecutor 七大参数是什么? 🔥

标准回答

corePoolSizemaximumPoolSizekeepAliveTimeunitworkQueuethreadFactoryhandler(拒绝策略)。任务先填满核心线程,再入队,队列满则扩到最大线程,仍满则触发拒绝策略。

面试官可能继续追问

  • Android 自建线程池注意什么?

    应显式命名 threadFactory(便于 Profiler 定位),队列有界防 OOM。

  • 核心线程会回收吗?

    默认不会;allowCoreThreadTimeOut(true) 可让核心线程超时回收。

  • 为什么 IO 密集和 CPU 密集池大小不同?

    CPU 密集≈核数;IO 密集可更大,因线程多在等 IO 而非占 CPU。


#11 线程池四种拒绝策略怎么选? ⭐

标准回答

AbortPolicy 抛异常(默认);CallerRunsPolicy 调用方线程执行,起背压;DiscardPolicy 静默丢弃;DiscardOldestPolicy 丢队首再提交。

面试官可能继续追问

  • Android 里拒绝策略怎么选?

    后台任务高峰时 CallerRunsPolicy 可减缓提交速度防雪崩;关键任务勿用 Discard,应降级或持久化队列。

  • OkHttp 用哪种策略?

    Dispatcher 自定义队列与并发上限,超额任务在队列等待而非简单拒绝。

  • 无界队列有什么问题?

    任务堆积导致内存暴涨,表现为卡顿而非立刻抛异常。


#12 ConcurrentHashMap JDK 8 实现原理? 🔥

标准回答

取消分段锁,采用 Node 数组 + 链表/红黑树,对桶头 synchronized 或 CAS 插入。sizeCtl 控制扩容,transfer 多线程协助迁移。读大多无锁(volatile 保证可见)。

面试官可能继续追问

  • Android 里 ConcurrentHashMap 怎么用?

    多线程缓存映射可用 CHM;key 为 int 时可评估 SparseArray 省装箱。

  • CHM 允许 null 键值吗?

    不允许,防歧义;HashMap 单线程场景才可以。

  • Collections.synchronizedMap 区别?

    CHM 桶级锁,并发读性能远好于全表一把锁。


#13 CountDownLatchCyclicBarrier 区别? ⭐

标准回答

CountDownLatch 一次性,计数减到零唤醒等待线程,常用于「等多路异步完成」;CyclicBarrier 可重用,多线程到齐再一起走,支持屏障动作。

面试官可能继续追问

  • Android 里 Latch/Barrier 怎么用?

    启动打点、多接口聚合刷新可用 Latch;周期性同步更常用协程 async/awaitAll

  • Latch 计数能加吗?

    不能,只能减;一次性语义。

  • 主线程 await 会 ANR 吗?

    会,主线程禁止 await 阻塞等待。


#14 AtomicInteger 等原子类解决什么问题? ⭐

标准回答

基于 CAS 无锁更新单个变量,避免 synchronized 重量级竞争。适合计数器、状态位、引用替换。

面试官可能继续追问

  • Android 里原子类怎么用?

    统计曝光次数、限流令牌可用原子类;复合逻辑仍需 AtomicReference 配合或加锁。

  • CAS 会自旋浪费 CPU 吗?

    高竞争下会;此时锁可能更合适。

  • LongAdderAtomicLong 怎么选?

    高并发累加 LongAdder 分段降低竞争;需要精确读取当前值用 AtomicLong


#15 Handler 为什么会导致内存泄漏?如何修复? 🔥

标准回答

非静态内部类 Handler 隐式持有外部 ActivityMessage 又持有 Handler,若延迟消息未处理,Activity 无法回收。

面试官可能继续追问

  • Android 里 Handler 泄漏怎么修?

    静态内部类 + WeakReference<Activity>、页面销毁时 removeCallbacksAndMessages(null)、优先 lifecycleScope 替代裸 Handler。

  • 主线程 Handler 也会泄漏吗?

    会,泄漏与是否主线程无关,取决于消息是否持有 Activity 引用链。

  • onDestroy 里必须清消息吗?

    使用了延迟/周期性消息时必须清;纯即时消息通常随队列消化。


核心层(7 题)

#16 happens-before 规则有哪些? 💡

标准回答

JMM 定义的可见性偏序:程序次序、监视器锁、volatile 写先于读、线程 start/join传递性。理解它才能解释「为什么 DCL 要 volatile」「为什么 synchronized 退出后其他线程能看见最新值」。

面试官可能继续追问

  • Android 里理解 happens-before 有什么用?

    读框架并发工具源码(如 ConcurrentHashMapHandler 同步屏障)都建立在 happens-before 之上。

  • 和「时间上的先后」是一回事吗?

    不是,happens-before 是内存可见性保证,不保证墙钟顺序。

  • final 字段发布安全吗?

    正确构造后,final 字段对其他线程可见,常用于不可变对象设计。


#17 AQS 是什么?ReentrantLock 如何依赖它? 💡

标准回答

AbstractQueuedSynchronizerstate + CLH 双向队列管理获取/释放同步状态。ReentrantLockSemaphoreCountDownLatch 内部都基于 AQS 定制 tryAcquire/tryRelease

面试官可能继续追问

  • Android 里需要了解 AQS 吗?

    虽不直接写 AQS,但读 MediaCodecCamera 等阻塞队列工具时有帮助。

  • state 为 0 表示什么?

    锁未被持有;ReentrantLock 重入时 state 递增。

  • 共享模式和独占模式区别?

    独占如锁;共享如 Semaphore 允许多个线程同时通过。


#18 双重检查锁单例有什么问题?如何正确实现? 💡

标准回答

DCL 意图减少 synchronized 开销,但未 volatile 时可能因指令重排发布半初始化对象。正确写法:volatile 实例 + 同步块双重检查。

面试官可能继续追问

  • Android 里单例怎么实现更好?

    更推荐枚举单例或 Application 级懒加载 + 依赖注入,DCL 多见于老旧 SDK 与面试题。

  • 枚举单例为什么更好?

    JVM 保证枚举实例唯一,且防反射/反序列化破坏。

  • Kotlin object 单例需要 DCL 吗?

    不需要,语言层保证线程安全懒初始化。


#19 Kotlin 协程和 Java 线程池在 Android 怎么选? 🔥

标准回答

IO/计算任务优先协程:lifecycleScope/viewModelScope 自动随生命周期取消,结构化并发避免泄漏。线程池适合 SDK 边界、Java 遗留模块、或需严格隔离线程名的场景(如 Glide)。

面试官可能继续追问

  • Android 里协程和线程池怎么选?

    不要 GlobalScope;阻塞式 Java API 在协程里用 withContext(Dispatchers.IO) 包装,而非主线程等待。

  • 协程能替代 Handler 吗?

    延迟/切主线程可用 delay + Dispatchers.Main;与 Choreographer 帧同步仍可能用 Handler。

  • 一个协程等于一个线程吗?

    不等于,多协程可复用少量线程,挂起时不占线程。


#20 为什么废弃 AsyncTask?现代替代方案? 🔥

标准回答

AsyncTask 线程池全局共享、串行/并行行为版本间不一致,易泄漏 Activity,且与生命周期脱节,API 30 起废弃。

面试官可能继续追问

  • Android 里 AsyncTask 替代方案?

    协程 + viewModelScopeExecutor + 主线程回调、WorkManager 做可持久后台任务。新代码禁止再引入 AsyncTask

  • WorkManager 和协程分工?

    需保证执行、重启、约束(网络/充电)用 WorkManager;页面内短时异步用协程。

  • 老项目大量 AsyncTask 怎么迁移?

    按边界逐步换协程,统一在 ViewModel 层发状态,避免在 Activity 内起任务。


#21 死锁如何产生?怎么排查? ⭐

标准回答

死锁需互斥、占有且等待、不可抢占、循环等待四条件。典型:线程 A 锁 m1m2,B 锁 m2m1

面试官可能继续追问

  • Android 里死锁怎么排查?

    jstack/Android Studio Profiler 看 Found one Java-level deadlock;预防:锁顺序一致、超时 tryLock、缩小锁粒度。线上优先从主线程阻塞与 Binder 同步调用链入手。

  • 数据库也会死锁吗?

    会,Room/SQLite 事务交叉更新可能死锁,需重试或统一访问顺序。

  • tryLock 失败怎么处理?

    降级、重试或上报,避免无限等待。


#22 OkHttp Dispatcher 线程模型是怎样的? 💡

标准回答

Dispatcher 管理就绪异步调用与 ExecutorService:最大 64 并发、每主机 5 并发(可配置),超额进队列等待。同步 execute 在调用线程执行。

面试官可能继续追问

  • Android 里 OkHttp Dispatcher 怎么理解?

    理解它可合理解释「为何大量图片请求不会无限开线程」。Glide 另有独立池,网络层不要与业务自建池混用同一无界队列。

  • 同步请求能在主线程吗?

    技术上能但会 ANR/卡顿,禁止主线程 execute

  • 如何与协程 suspend 配合?

    suspendCancellableCoroutine 包装 enqueue,取消时 call.cancel()


面试策略速查

面试等级 建议掌握
初级 ★★★☆☆
中级 ★★★★★
高级 ★★★★★

必背知识(🔥)

主线程规则、synchronized/volatile、ThreadPool 七大参数、CHM、Handler 泄漏、协程 vs 线程池、AsyncTask 废弃原因

高频追问

「volatile 能保证原子吗」「线程池队列满了怎么办」「Handler 怎么防泄漏」「协程和线程区别」

加分项(💡)

happens-before、AQS 与 ReentrantLock 关系、DCL 与 volatile、OkHttp Dispatcher 并发上限

容易踩坑

主线程 sleep/wait/网络请求;无界队列线程池 OOM;非静态 Handler 泄漏;用 volatilei++;新代码继续用 AsyncTask


完整链路一句通

并发题一条线:主线程只管 UI 事件循环,共享状态用 synchronized/volatile/原子类/CHM 保证可见与互斥,耗时任务进线程池或协程 IO 线程,结果回主线程,页面销毁必须取消任务并清 Handler 消息,泄漏与 ANR 从这条链路上排查。


相关推荐

HashMap、mutableMapOf 与 ConcurrentHashMap 完全指南

Android 高级工程师面试:Java 基础知识 近1年高频追问 22 题

相关推荐
要开心吖ZSH1 小时前
Java事务与MySQL事务的关系及MVCC通俗解析
java·开发语言·mysql·mvcc
放弃 治疗1 小时前
Windows 11系统 最新 Launch4j 安装与使用教程:从 JAR 到 EXE 的完整打包指南
java·jar
火星校尉1 小时前
一场数据基建与消费场景的跨界实验
java·前端·数据库·python·php
2501_943782351 小时前
【共创季稿事节】摩斯电码转换器:编码表与双向转换的实现
android·华为·鸿蒙·鸿蒙系统
寻道码路1 小时前
LangChain4j Java AI 应用开发实战(二十六):多模型集成策略 —— OpenAI、DeepSeek、阿里百炼混合使用
java·开发语言·人工智能·ai
STCNXPARM1 小时前
Android selinux详解
android·selinux
jzwalliser2 小时前
安卓手机玩转Manim动画制作
android·manim
ch.ju2 小时前
Java Programming Chapter 4——Static code block
java·开发语言
risc1234562 小时前
Lucene80DocValuesConsumer 五种类型源码阅读顺序
java·服务器·前端