文章目录
- 学习建议
- 基础层(8 题)
- #1 线程有哪些创建方式?Android 里怎么选? ⭐
- 标准回答
- 面试官可能继续追问
- #2 线程有哪些状态?如何转换? ⭐
- 标准回答
- 面试官可能继续追问
- #3 为什么 Android 主线程不能做耗时操作? 🔥
- 标准回答
- 面试官可能继续追问
- #4 `synchronized` 的原理是什么?锁升级过程? 🔥
- 标准回答
- 面试官可能继续追问
- #5 `volatile` 的语义是什么?能保证原子性吗? 🔥
- 标准回答
- 面试官可能继续追问
- #6 `synchronized` 和 `volatile` 怎么选? ⭐
- 标准回答
- 面试官可能继续追问
- #7 `wait/notify` 和 `sleep` 有什么区别? ⭐
- 标准回答
- 面试官可能继续追问
- #8 什么是线程安全?Android 里如何保证? ⭐
- 标准回答
- 面试官可能继续追问
- 进阶层(7 题)
- #9 `ReentrantLock` 和 `synchronized` 怎么选? 🔥
- 标准回答
- 面试官可能继续追问
- #10 `ThreadPoolExecutor` 七大参数是什么? 🔥
- 标准回答
- 面试官可能继续追问
- #11 线程池四种拒绝策略怎么选? ⭐
- 标准回答
- 面试官可能继续追问
- #12 `ConcurrentHashMap` JDK 8 实现原理? 🔥
- 标准回答
- 面试官可能继续追问
- #13 `CountDownLatch` 和 `CyclicBarrier` 区别? ⭐
- 标准回答
- 面试官可能继续追问
- #14 `AtomicInteger` 等原子类解决什么问题? ⭐
- 标准回答
- 面试官可能继续追问
- #15 `Handler` 为什么会导致内存泄漏?如何修复? 🔥
- 标准回答
- 面试官可能继续追问
- 核心层(7 题)
- #16 `happens-before` 规则有哪些? 💡
- 标准回答
- 面试官可能继续追问
- #17 AQS 是什么?`ReentrantLock` 如何依赖它? 💡
- 标准回答
- 面试官可能继续追问
- #18 双重检查锁单例有什么问题?如何正确实现? 💡
- 标准回答
- 面试官可能继续追问
- #19 Kotlin 协程和 Java 线程池在 Android 怎么选? 🔥
- 标准回答
- 面试官可能继续追问
- #20 为什么废弃 `AsyncTask`?现代替代方案? 🔥
- 标准回答
- 面试官可能继续追问
- #21 死锁如何产生?怎么排查? ⭐
- 标准回答
- 面试官可能继续追问
- #22 OkHttp `Dispatcher` 线程模型是怎样的? 💡
- 标准回答
- 面试官可能继续追问
- 面试策略速查
- 必背知识(🔥)
- 高频追问
- 加分项(💡)
- 容易踩坑
- 完整链路一句通
- 相关推荐
学习建议
| 目标层级 | 建议 |
|---|---|
| 初级 | 掌握基础层 8 题:主线程规则、synchronized/volatile 区别、线程状态;工程联想看追问第 1 条 |
| 中级 | 通读全文;进阶层能讲清线程池七大参数、CHM 桶级锁、Handler 泄漏成因与修复 |
| 高级 | 核心层加分项必答:happens-before、AQS、DCL;能串联「同步原语→线程池→协程→泄漏排查」完整链路 |
基础层(8 题)
#1 线程有哪些创建方式?Android 里怎么选? ⭐
标准回答
常见四种:Thread、Runnable、Callable+Future、线程池。裸new Thread难管控生命周期与数量,工程上应优先Executor/ThreadPoolExecutor。
面试官可能继续追问
Android 里线程怎么选?
网络用 OkHttp 内置 Dispatcher,图片用 Glide 线程池,业务异步优先 Kotlin 协程 +viewModelScope,避免无限创建线程导致 OOM。为什么不推荐到处
new Thread?
无线程复用、无队列背压,高并发时创建销毁开销大且难统一取消。协程和线程池是什么关系?
协程是用户态任务调度,底层常跑在线程池的少量线程上,不是 1:1 替代关系。
#2 线程有哪些状态?如何转换? ⭐
标准回答
六种:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。等锁进BLOCKED,wait()/join()进WAITING,sleep()/带超时wait进TIMED_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 也遵循主线程更新状态规则。
#4synchronized的原理是什么?锁升级过程? 🔥
标准回答
synchronized基于监视器锁(Monitor),字节码层有monitorenter/monitorexit,保证互斥与可见性。JDK 偏向锁→轻量锁→重量锁逐级升级,竞争加剧时膨胀为 OS 互斥量。
面试官可能继续追问
Android 里
synchronized怎么用?
多线程写共享缓存、单例初始化时常用;注意锁粒度,避免在 UI 路径锁整个大对象。锁的是对象还是代码?
锁的是对象监视器;实例方法锁this,静态方法锁Class对象。和
ReentrantLock最大区别?synchronizedJVM 自动释放;ReentrantLock可中断、可超时、支持公平锁与Condition。
#5volatile的语义是什么?能保证原子性吗? 🔥
标准回答
volatile保证可见性与禁止指令重排,不保证复合操作原子性(如i++)。适用单一状态标志位,如「请求已取消」「配置已刷新」。
面试官可能继续追问
Android 里
volatile怎么用?
DCL 单例常配合volatile防重排;多字段一致性仍用synchronized或原子类。volatile和synchronized性能差多少?
无竞争时volatile更轻;有互斥需求必须用锁或原子类。DCL 单例为什么需要
volatile?
防new对象指令重排导致其他线程拿到未初始化完成实例。
#6synchronized和volatile怎么选? ⭐
标准回答
要互斥改共享变量用synchronized或Lock/Atomic;只发布单一可见状态用volatile。
面试官可能继续追问
Android 里
synchronized和volatile怎么选?
「下载取消标志」可用volatile boolean;「计数器累加」用AtomicInteger或锁,单用volatile会丢更新。多个
volatile字段能保证组合原子吗?
不能,读写之间可能被其他线程插入,需整体加锁。Kotlin 里还需要手写
volatile吗?
Java 字段可用@Volatile;多数场景用协程/原子 API 更清晰。
#7wait/notify和sleep有什么区别? ⭐
标准回答
wait/notify必须在同步块内调用,释放锁并进入等待队列;notify唤醒后需重新竞争锁。sleep不释放锁,仅暂停当前线程指定时间。
面试官可能继续追问
Android 里还用
wait/notify吗?
业务很少手写,多用CountDownLatch、BlockingQueue或协程suspend;但理解有助于读 JDK/框架源码。为什么
wait要在while循环里?
防虚假唤醒,被唤醒后应再检查条件。notify和notifyAll怎么选?
不确定哪个线程满足条件时用notifyAll,避免饿死。
#8 什么是线程安全?Android 里如何保证? ⭐
标准回答
多线程并发访问下仍保持正确语义即线程安全。手段:不可变对象、线程封闭(主线程 UI)、同步锁、并发容器、原子类。
面试官可能继续追问
Android 里如何保证线程安全?
RecyclerView适配器数据源若在后台改、主线程读,需 Copy-on-Write 或主线程统一调度;SparseArray非线程安全,多线程应加锁或换ConcurrentHashMap。StringBuilder线程安全吗?
不安全;多线程拼接用StringBuffer或局部StringBuilder不外泄。主线程算线程安全吗?
单线程内天然安全,但 Handler 延迟任务交叉时仍可能踩共享状态。
进阶层(7 题)
#9ReentrantLock和synchronized怎么选? 🔥
标准回答
都能互斥。ReentrantLock支持可中断、尝试锁、公平锁、Condition多条件队列,需手动unlock且最好在finally释放。synchronized语法简单、JVM 优化成熟。
面试官可能继续追问
Android 里
ReentrantLock和synchronized怎么选?
简单临界区用synchronized;需要超时获取或精细唤醒用ReentrantLock。公平锁会降低吞吐吗?
会,严格 FIFO 排队减少插队,高竞争下吞吐通常低于非公平锁。忘记
unlock会怎样?
其他线程永久阻塞,类似死锁;必须try-finally释放。
#10ThreadPoolExecutor七大参数是什么? 🔥
标准回答
corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler(拒绝策略)。任务先填满核心线程,再入队,队列满则扩到最大线程,仍满则触发拒绝策略。
面试官可能继续追问
Android 自建线程池注意什么?
应显式命名threadFactory(便于 Profiler 定位),队列有界防 OOM。核心线程会回收吗?
默认不会;allowCoreThreadTimeOut(true)可让核心线程超时回收。为什么 IO 密集和 CPU 密集池大小不同?
CPU 密集≈核数;IO 密集可更大,因线程多在等 IO 而非占 CPU。
#11 线程池四种拒绝策略怎么选? ⭐
标准回答
AbortPolicy抛异常(默认);CallerRunsPolicy调用方线程执行,起背压;DiscardPolicy静默丢弃;DiscardOldestPolicy丢队首再提交。
面试官可能继续追问
Android 里拒绝策略怎么选?
后台任务高峰时CallerRunsPolicy可减缓提交速度防雪崩;关键任务勿用Discard,应降级或持久化队列。OkHttp 用哪种策略?
Dispatcher 自定义队列与并发上限,超额任务在队列等待而非简单拒绝。无界队列有什么问题?
任务堆积导致内存暴涨,表现为卡顿而非立刻抛异常。
#12ConcurrentHashMapJDK 8 实现原理? 🔥
标准回答
取消分段锁,采用Node数组 + 链表/红黑树,对桶头synchronized或 CAS 插入。sizeCtl控制扩容,transfer多线程协助迁移。读大多无锁(volatile保证可见)。
面试官可能继续追问
Android 里
ConcurrentHashMap怎么用?
多线程缓存映射可用 CHM;key 为 int 时可评估SparseArray省装箱。CHM 允许
null键值吗?
不允许,防歧义;HashMap单线程场景才可以。和
Collections.synchronizedMap区别?
CHM 桶级锁,并发读性能远好于全表一把锁。
#13CountDownLatch和CyclicBarrier区别? ⭐
标准回答
CountDownLatch一次性,计数减到零唤醒等待线程,常用于「等多路异步完成」;CyclicBarrier可重用,多线程到齐再一起走,支持屏障动作。
面试官可能继续追问
Android 里 Latch/Barrier 怎么用?
启动打点、多接口聚合刷新可用 Latch;周期性同步更常用协程async/awaitAll。Latch 计数能加吗?
不能,只能减;一次性语义。主线程
await会 ANR 吗?
会,主线程禁止await阻塞等待。
#14AtomicInteger等原子类解决什么问题? ⭐
标准回答
基于 CAS 无锁更新单个变量,避免synchronized重量级竞争。适合计数器、状态位、引用替换。
面试官可能继续追问
Android 里原子类怎么用?
统计曝光次数、限流令牌可用原子类;复合逻辑仍需AtomicReference配合或加锁。CAS 会自旋浪费 CPU 吗?
高竞争下会;此时锁可能更合适。LongAdder和AtomicLong怎么选?
高并发累加LongAdder分段降低竞争;需要精确读取当前值用AtomicLong。
#15Handler为什么会导致内存泄漏?如何修复? 🔥
标准回答
非静态内部类Handler隐式持有外部Activity,Message又持有Handler,若延迟消息未处理,Activity 无法回收。
面试官可能继续追问
Android 里 Handler 泄漏怎么修?
静态内部类 +WeakReference<Activity>、页面销毁时removeCallbacksAndMessages(null)、优先lifecycleScope替代裸 Handler。主线程 Handler 也会泄漏吗?
会,泄漏与是否主线程无关,取决于消息是否持有 Activity 引用链。onDestroy里必须清消息吗?
使用了延迟/周期性消息时必须清;纯即时消息通常随队列消化。
核心层(7 题)
#16happens-before规则有哪些? 💡
标准回答
JMM 定义的可见性偏序:程序次序、监视器锁、volatile写先于读、线程start/join、传递性。理解它才能解释「为什么 DCL 要volatile」「为什么synchronized退出后其他线程能看见最新值」。
面试官可能继续追问
Android 里理解 happens-before 有什么用?
读框架并发工具源码(如ConcurrentHashMap、Handler同步屏障)都建立在 happens-before 之上。和「时间上的先后」是一回事吗?
不是,happens-before 是内存可见性保证,不保证墙钟顺序。final字段发布安全吗?
正确构造后,final字段对其他线程可见,常用于不可变对象设计。
#17 AQS 是什么?ReentrantLock如何依赖它? 💡
标准回答
AbstractQueuedSynchronizer用state+ CLH 双向队列管理获取/释放同步状态。ReentrantLock、Semaphore、CountDownLatch内部都基于 AQS 定制tryAcquire/tryRelease。
面试官可能继续追问
Android 里需要了解 AQS 吗?
虽不直接写 AQS,但读MediaCodec、Camera等阻塞队列工具时有帮助。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替代方案?
协程 +viewModelScope、Executor+ 主线程回调、WorkManager做可持久后台任务。新代码禁止再引入AsyncTask。WorkManager和协程分工?
需保证执行、重启、约束(网络/充电)用WorkManager;页面内短时异步用协程。老项目大量
AsyncTask怎么迁移?
按边界逐步换协程,统一在ViewModel层发状态,避免在Activity内起任务。
#21 死锁如何产生?怎么排查? ⭐
标准回答
死锁需互斥、占有且等待、不可抢占、循环等待四条件。典型:线程 A 锁m1等m2,B 锁m2等m1。
面试官可能继续追问
Android 里死锁怎么排查?
jstack/Android Studio Profiler 看Found one Java-level deadlock;预防:锁顺序一致、超时tryLock、缩小锁粒度。线上优先从主线程阻塞与 Binder 同步调用链入手。数据库也会死锁吗?
会,Room/SQLite 事务交叉更新可能死锁,需重试或统一访问顺序。tryLock失败怎么处理?
降级、重试或上报,避免无限等待。
#22 OkHttpDispatcher线程模型是怎样的? 💡
标准回答
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 泄漏;用volatile做i++;新代码继续用 AsyncTask
完整链路一句通
并发题一条线:主线程只管 UI 事件循环,共享状态用 synchronized/volatile/原子类/CHM 保证可见与互斥,耗时任务进线程池或协程 IO 线程,结果回主线程,页面销毁必须取消任务并清 Handler 消息,泄漏与 ANR 从这条链路上排查。
相关推荐
HashMap、mutableMapOf 与 ConcurrentHashMap 完全指南
Android 高级工程师面试:Java 基础知识 近1年高频追问 22 题