JAVA多线程面试题
八股
synchronized 的锁升级过程
无锁 ↓(线程获得锁) 偏向锁(记录线程ID) ↓(出现其他线程竞争) 撤销偏向锁 ↓(CAS 成功) 轻量级锁(用户态自旋锁) ↓(CAS 多次失败) 重量级锁(内核态阻塞锁)
面试总结
- 面试官:介绍一下new线程的几种方式
我:
- 继承Thread 最简单的
- 实现 Runnable 接口,需要重写run方法
- 继承 Callable 接口 + FutureTask (有返回值)
面试官: 三个线程,依次打印数字 1 ~ 9,每个线程打印一个数字,三个线程之间 轮流执行,形成交替打印的效果。简单说一下思路
我:
我会声明两个公共变量,一个记录线程id和应该打印的数字,并使用锁去锁住代码块,每一个线程过来的时候都需要拥有锁,并且判断线程id必须和公共变量的线程id一致才能打印,打印之后线程id会自增,并唤醒所有等待的线程,再次去竞争锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45public class AlternatePrint {
private static final Object lock = new Object();
private static int number = 1; // 当前要打印的数字
private static int threadFlag = 1; // 当前该哪个线程执行,取值为1/2/3
public static void main(String[] args) {
Runnable task1 = () -> printNumber(1);
Runnable task2 = () -> printNumber(2);
Runnable task3 = () -> printNumber(3);
new Thread(task1, "线程1").start();
new Thread(task2, "线程2").start();
new Thread(task3, "线程3").start();
}
public static void printNumber(int threadId) {
while (true) {
synchronized (lock) {
// 如果当前不是该线程打印,进入等待
while (threadFlag != threadId) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 如果数字超过9,结束所有线程
if (number > 9) {
lock.notifyAll();
break;
}
System.out.println(Thread.currentThread().getName() + " 打印: " + number);
number++;
// 设置下一个线程标识:1 -> 2 -> 3 -> 1
threadFlag = threadId % 3 + 1;
// 唤醒所有线程
lock.notifyAll();
}
}
}
}面试官:线程之间的通信方式有什么
我:
- ✅ 1. 共享变量通信(最常用)
示例:使用 volatile 关键字保证可见性 - ✅ 2. wait() / notify() / notifyAll()(配合 synchronized 使用)
- Lock + Condition(更灵活的等待/通知机制)
- java.util.concurrent.locks.Condition 提供比 wait/notify 更灵活的方式,如多个条件队列。
1
2
3
4
5
6
7
8
9
10
11
12
13Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
while (!conditionMet) {
condition.await(); // 相当于 wait()
}
// do something
condition.signal(); // 相当于 notify()
} finally {
lock.unlock();
}
- java.util.concurrent.locks.Condition 提供比 wait/notify 更灵活的方式,如多个条件队列。
- Lock + Condition(更灵活的等待/通知机制)
- ✅ 4. join() 方法
- 让一个线程等待另一个线程执行完后再继续。
1
2
3
4
5
6Thread t = new Thread(() -> {
System.out.println("Child thread running");
});
t.start();
t.join(); // 当前线程等 t 执行完
System.out.println("Main thread resumes");
- 让一个线程等待另一个线程执行完后再继续。
✅ 5. 并发工具类(推荐使用)
| 类名 | 功能说明 |
| ———————— | —————————————————————————————————— |
|BlockingQueue| 如ArrayBlockingQueue、LinkedBlockingQueue,线程安全队列,适用于生产者-消费者模型 |
|CountDownLatch| 等待多个线程完成后再继续执行 |
|CyclicBarrier| 等待多个线程到达某一屏障点后一起执行 |
|Semaphore| 控制并发线程数量 |
|Exchanger| 两个线程之间交换数据 |
- ✅ 1. 共享变量通信(最常用)
面试官:线程池的拒绝策略有哪些
- 我:
策略类 行为说明 AbortPolicy(默认)抛出 RejectedExecutionException异常CallerRunsPolicy谁提交任务,谁就自己去执行(同步) DiscardPolicy直接丢弃任务,不抛异常 DiscardOldestPolicy丢弃队列中最老的任务,尝试提交当前任务
- 面试官:进程通信的有什么方式
- 我:
使用数据库和redis
Socket 通信 例如 netty
- 适合多数 Java 多进程或多 JVM 程序之间通信。
- 读写文件
- 一个写、一个读
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Little Monste'Blog!
评论




