02-并发编程面试题
对应原始资料:
12-BAT/08-并发编程篇、07-常见集合篇
一、线程基础
Q1:创建线程的方式
继承 Thread、实现 Runnable、实现 Callable(有返回值 + FutureTask)、线程池。
Q2:start 和 run 区别
start()启动新线程并自动调 run。- 直接调
run()是普通方法调用,不开新线程。
Q3:线程状态(6 种)
NEW → RUNNABLE → (BLOCKED / WAITING / TIMED_WAITING) → TERMINATED。
Q4:sleep vs wait
| sleep | wait | |
|---|---|---|
| 所属 | Thread | Object |
| 释放锁 | 否 | 是 |
| 使用位置 | 任意 | synchronized 块内 |
| 唤醒 | 时间到 | notify/notifyAll |
二、synchronized
Q5:synchronized 用法和锁对象
- 修饰实例方法:锁 this。
- 修饰静态方法:锁 Class 对象。
- 同步代码块:锁指定对象。
Q6:synchronized 底层原理
- 代码块:
monitorenter/monitorexit字节码指令。 - 方法:
ACC_SYNCHRONIZED标志。 - 锁升级(JDK 6+):无锁 → 偏向锁 → 轻量级锁 → 重量级锁。
- 偏向锁:单线程访问,CAS 设线程 ID。
- 轻量级锁:多线程交替,自旋。
- 重量级锁:竞争激烈,OS 互斥量。
三、volatile
Q7:volatile 两个作用
- 可见性:写后立即刷主存,读从主存读。
- 禁止指令重排:内存屏障。
- 不保证原子性(
i++仍线程不安全)。
Q8:单例为什么要 volatile
new 不是原子,分三步:① 分配内存 ② 初始化 ③ 赋值。若重排成 ①③②,其他线程可能拿到未初始化对象。volatile 禁止重排。
四、CAS 与 AQS
Q9:CAS 原理
Compare And Swap:比较并交换。
预期值 == 内存值 ? 把内存设为新值 : 失败重试底层 Unsafe.compareAndSwap,CPU cmpxchg 指令保证原子。
- ABA 问题:用
AtomicStampedReference(版本号)。
Q10:AQS(AbstractQueuedSynchronizer)
- ReentrantLock、Semaphore、CountDownLatch 的底层框架。
- 核心:
state(同步状态)+ CLH 双向队列。 - 独占模式 / 共享模式。
五、ReentrantLock vs synchronized
| 维度 | synchronized | ReentrantLock |
|---|---|---|
| 释放 | 自动 | 手动 unlock(finally) |
| 中断 | 不可中断 | lockInterruptibly() |
| 超时 | 不支持 | tryLock(timeout) |
| 公平 | 非公平 | 可选公平 |
| 条件 | 一个 wait set | 多个 Condition |
六、线程池
Q11:ThreadPoolExecutor 七参数与执行流程
详见 02-Java进阶/05。 核心线程 → 队列 → 非核心线程 → 拒绝策略。
Q12:为什么不推荐 Executors
newFixedThreadPool/newSingleThreadExecutor:队列LinkedBlockingQueue无界,易 OOM。newCachedThreadPool:max 是 Integer.MAX_VALUE,线程数无限,易 OOM。- 用 ThreadPoolExecutor 显式指定参数。
Q13:核心线程数设置
- CPU 密集:N + 1。
- IO 密集:2N 或更多。
七、并发工具
Q14:CountDownLatch vs CyclicBarrier
CountDownLatch:一次性,等待 N 个完成(主线程等待子任务)。CyclicBarrier:可重置,N 个线程互相等待到齐。
Q15:Semaphore
信号量,控制同时访问的线程数(限流)。
Q16:ThreadLocal
- 线程私有变量副本。
- 底层:
ThreadLocalMap(key 是 ThreadLocal 弱引用)。 - 内存泄漏:value 是强引用,线程不结束不释放;用完
remove()。
八、JUC 集合
Q17:HashMap 多线程下问题
- JDK 7:扩容采用头插法,可能死循环。
- JDK 8:尾插法不死循环,但仍可能丢数据。
- 并发用 ConcurrentHashMap。
Q18:ConcurrentHashMap 实现
- JDK 7:分段锁 Segment(16 段)。
- JDK 8:
数组 + 链表/红黑树+ CAS + synchronized(锁桶头节点)。
Q19:CopyOnWriteArrayList
写时复制:写时加锁复制出新数组,读不加锁。适合读多写少。
九、死锁
Q20:死锁四条件
互斥、请求保持、不剥夺、循环等待。
Q21:如何避免
固定锁顺序、tryLock 超时、减小锁粒度。
Q22:怎么排查
jstack <pid>查线程堆栈,找 "Found one Java-level deadlock"。- arthas
thread -b查阻塞。
十、原子类
| 类 | 用途 |
|---|---|
| AtomicInteger/Long | 基本类型原子 |
| AtomicReference | 引用类型 |
| AtomicStampedReference | 带版本号(ABA) |
| LongAdder | 高并发计数(分段累加) |
高频考点速记
- synchronized 锁升级:偏向 → 轻量 → 重量。
- volatile:可见性 + 禁止重排,不保证原子。
- CAS:比较交换,ABA 用版本号。
- AQS = state + CLH 队列。
- 线程池流程:核心 → 队列 → 非核心 → 拒绝。
- ConcurrentHashMap JDK 8 锁桶头节点。