整理自多线程面试题 59 道。深度学习见 Java 并发系列 · 扩展 并发编程面试 123 道。
相关学习笔记
| 专题 | 笔记 |
|---|---|
| 线程基础 | 线程基础 |
| 生命周期与通信 | 生命周期与通信 |
| JMM 与 volatile | JMM 与 volatile |
| synchronized 与 Lock | synchronized 与 Lock |
| Atomic 与 CAS | Atomic 与 CAS |
| 线程池 | 线程池 |
| 并发工具 | 并发工具类 |
| ThreadLocal | ThreadLocal |
| 并发容器 | 并发容器 |
目录
基础概念
1. 多线程有什么用?
① 发挥多核 CPU 优势,真正并行;② 防止阻塞(单线程 IO 阻塞会拖死整个程序);③ 便于建模,大任务拆小任务。→ 线程基础
2. 线程和进程的区别?
进程是资源分配单位,有独立地址空间;线程是调度单位,共享进程堆等资源,有独立栈。进程切换开销大、更健壮;线程轻量但一崩可能拖垮进程。→ 线程基础
3. Java 实现线程有哪几种方式?
继承 Thread、实现 Runnable、Callable+Future、线程池提交任务。生产优先线程池。
4. start() 和 run() 有什么区别?
start() 启动新线程由 JVM 调用 run(),多线程交替/并行;直接 run() 只是当前线程普通方法调用。
5. 怎么终止线程?如何优雅终止?
废弃 stop()。用 interrupt + 正确处理中断,或 volatile 标志 协作退出。
6. 线程生命周期有哪几种状态?如何流转?
NEW → RUNNABLE ⇄ BLOCKED/WAITING/TIMED_WAITING → TERMINATED。→ 生命周期
7. wait() 和 sleep() 有什么区别?
sleep 不释放锁,属 Thread;wait 释放对象监视器,属 Object,需 notify 唤醒。
8. 多线程同步有哪几种方法?
synchronized、Lock(ReentrantLock 等)、分布式锁;读多写少可用 ReadWriteLock。
9. 什么是死锁?如何避免?
两线程互相等待对方锁。避免:固定加锁顺序、缩小锁范围、tryLock 超时、避免嵌套多锁。
10. 多线程之间如何通信?
wait/notify/notifyAll、BlockingQueue、Condition、CountDownLatch 等。
11. 线程怎样拿到返回结果?
实现 Callable,配合 Future/FutureTask 或线程池 submit。
12. volatile 关键字的作用?
保证可见性;禁止特定指令重排序。不保证 i++ 等复合操作原子性;常与 CAS 配合。→ JMM 与 volatile
13. T1、T2、T3 如何保证按顺序执行?
链式 join():t1.start(); t1.join(); t2.start(); t2.join(); …
14. 怎么控制同一时间只有 3 个线程运行?
Semaphore(3),acquire() / release()。
20. 什么是原子性、可见性、有序性?
原子性:操作不可分割;可见性:修改对其他线程可见;有序性:程序执行顺序感知。→ JMM
21. 什么是守护线程?有什么用?
守护线程为其他线程服务,JVM 在所有用户线程结束后退出。setDaemon(true)。
22. 线程运行时发生异常会怎样?
未捕获则线程终止;可通过 UncaughtExceptionHandler 统一处理。
23. yield() 有什么用?
提示让出 CPU,不保证其他线程立即运行。
43. 多线程上下文切换是什么?
CPU 从一线程切换到另一线程,保存/恢复上下文,有开销。
44. Java 用什么线程调度算法?
抢占式;高优先级/饥饿等因素影响时间片分配。
45. Thread.sleep(0) 的作用?
可能触发重新分配时间片,平衡低优先级线程机会。
46. Java 内存模型?哪些共享、哪些不共享?
堆、方法区共享;栈、PC、本地方法栈线程私有。→ JMM
47. 乐观锁和悲观锁?
悲观:先加锁再操作(synchronized)。乐观:CAS 比较交换,失败重试。
51. Runnable 和 Thread 用哪个好?
优先 Runnable,避免占用唯一继承位,便于线程池。
56. Java 中堆和栈有什么不同?
堆共享存对象;每线程独立栈存局部变量、栈帧。堆上共享变量需同步或 volatile。
57. 如何获取线程堆栈?
jstack pid、Linux kill -3、JVisualVM/JConsole 等。
58. 如何创建线程安全的单例?
饿汉式、静态内部类、枚举;懒汉 DCL 需 volatile 实例引用。→ DCL
59. 什么是阻塞式方法?
结果返回前当前线程挂起,如 accept()、read()、Future.get()。
创建与生命周期
(见上 3–7、21–23、43–46、51、56–59)
同步与通信
19. 什么是活锁、饥饿、无锁、死锁?
死锁:互相等锁;活锁:互相谦让都不前进;饥饿:长期拿不到 CPU/锁;无锁:CAS 循环改共享值,无阻塞。→ synchronized 与 Lock
24. 什么是重入锁?
同一线程可多次获取同一把锁。synchronized、ReentrantLock 均可重入。
25. synchronized 有哪几种用法?
锁实例方法、锁静态方法、锁代码块(指定对象)。块范围越小越好。
32. 线程同步需要注意什么?
缩小同步范围;分布式场景用分布式锁;固定加锁顺序防死锁。
33. wait() 使用前提?
必须在持有对象监视器的 synchronized 块内。
36. 保证可见性有哪几种方式?
synchronized、volatile、Lock。
48. Hashtable 的 size() 为什么要同步?
put 与 size 并发时,非同步 size 可能读到中间态;同步保证一致性。
49. 同步方法和同步块哪种更好?
同步块更好,锁外代码仍可并发,原则:同步范围越小越好。
50. 什么是自旋锁?
线程循环 CAS 尝试获锁而不挂起;适合锁持有极短。
52. notify 和 notifyAll 区别?
notify 唤醒一个等待线程;notifyAll 唤醒全部,由锁竞争决定谁执行。
53. 为什么 wait/notify 在 Object 而不是 Thread?
锁是对象级;wait 表示等待某个对象的锁条件。
54. 为什么 wait/notify 要在同步块中?
API 强制,否则 IllegalMonitorStateException;也避免 wait/notify 竞态。
55. 为什么要在循环中检查 wait 条件?
防止伪唤醒;被唤醒时条件可能已变,需 while 再判断。
JMM 与锁
29. 什么是 CAS?多线程中哪些应用?
Compare-And-Swap,原子比较替换。java.util.concurrent.atomic 包、ConcurrentHashMap 部分操作。→ Atomic 与 CAS
30. 怎么检测线程是否拥有锁?
Thread.holdsLock(obj)。
37. 常用的 Lock 实现?
ReentrantLock、ReentrantReadWriteLock。
38. ThreadLocal 是什么?应用场景?
线程内局部变量;数据库连接(老)、SimpleDateFormat、用户上下文。线程池场景必须 remove。→ ThreadLocal
39. ReadWriteLock 有什么用?
读共享、写独占,读多写少提升吞吐。
40. FutureTask 是什么?
可提交线程池的 Runnable,同时是 Future,获取 Callable 异步结果。
41. 怎么唤醒阻塞线程?
wait/sleep/join 阻塞可 interrupt;IO 阻塞无法被 Java 直接唤醒。
42. 不可变对象对多线程有什么帮助?
安全发布后可无锁读,无需额外同步。
线程池与工具
15. 为什么要使用线程池?
复用线程、控制数量,避免频繁创建销毁和线程过多耗尽资源。→ 线程池
16. 常用线程池及原理?
Fixed/Cached/Single/Scheduled;底层 ThreadPoolExecutor:core/max/队列/拒绝策略。
17. submit() 和 execute() 区别?
execute 无返回值;submit 返回 Future,可 get 结果和异常。
18. CyclicBarrier 和 CountDownLatch 区别?
Latch 一个等多个、一次性;Barrier 多个互相等到齐、可重用。→ 并发工具
26. Fork/Join 框架干什么?
大任务拆分并行,再合并结果。
27. 线程数过多会造成什么异常?
栈溢出、OOM、GC 压力等。
28. 线程安全和不安全的集合?
HashMap、ArrayList 非线程安全;Vector、Hashtable 同步但性能差;推荐 ConcurrentHashMap 等。→ 并发容器
34. Fork/Join 使用注意?
拆分过深线程堆积;递归过深栈溢出。
35. 线程之间如何传递数据?
共享对象 + 同步;BlockingQueue 生产者-消费者。
60. 提交任务时线程池队列已满会怎样?
未达 max 则新建线程;达 max 且队列满则执行拒绝策略(Abort/CallerRuns 等)。
排查与其他
31. JDK 排查多线程用什么命令?
jstack 打印线程栈;配合 jps 查 pid。
速记
| 主题 | 关键词 |
|---|---|
| 创建 | Runnable、Callable、线程池 |
| 协作 | wait/notify + while、synchronized |
| 可见性 | volatile、synchronized |
| 原子 | Atomic、CAS、锁 |
| 限流 | Semaphore、线程池 max |
| 排查 | jstack、jconsole |