Java面试通关指南(二):并发编程篇
Java并发编程是面试重点和难点,涉及基础概念、线程状态、锁机制和同步工具。主要内容包括:1) 并发与并行的区别;2) 线程状态转换(NEW→RUNNABLE→WAITING→TERMINATED);3) synchronized实现原理及锁升级过程(偏向锁→轻量级锁→重量级锁);4) volatile的内存可见性和禁止指令重排序特性;5) AQS框架实现同步的核心机制。掌握这些知识对开发高性能、
前言
并发编程是Java面试中的难点和重点,也是区分初中高级程序员的关键。
掌握并发不仅是为了面试,更是为了写出高性能、高可用的系统。
一、Java并发基础
1.1 并发 vs 并行
面试高频辨析:并发和并行的区别是什么?
java
// 并发:同一时间段内多个任务交替执行(单核CPU)
// 并行:同一时刻多个任务同时执行(多核CPU)
// 生活类比:
// 并发:一个咖啡师交替服务多个顾客
// 并行:多个咖啡师同时服务多个顾客
1.2 线程状态转换
java
public class ThreadStateDemo {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000); // TIMED_WAITING
synchronized (ThreadStateDemo.class) {
ThreadStateDemo.class.wait(); // WAITING
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("创建后: " + thread.getState()); // NEW
thread.start();
System.out.println("启动后: " + thread.getState()); // RUNNABLE
Thread.sleep(100);
System.out.println("sleep中: " + thread.getState()); // TIMED_WAITING
Thread.sleep(2000);
synchronized (ThreadStateDemo.class) {
ThreadStateDemo.class.notify();
}
thread.join();
System.out.println("结束后: " + thread.getState()); // TERMINATED
}
}
线程生命周期图:
text
NEW → RUNNABLE → (BLOCKED/WAITING/TIMED_WAITING) → TERMINATED
↑ ↓
←←←←←←←←←←←←←
二、synchronized深度解析
2.1 实现原理
面试题:synchronized底层是如何实现的?
java
// 字节码层面分析
public class SynchronizedDemo {
// 同步代码块
public void method1() {
synchronized (this) {
System.out.println(“同步代码块”);
}
}
// 同步方法
public synchronized void method2() {
System.out.println("同步方法");
}
}
编译后的字节码:
text
method1():
monitorenter // 进入同步块
// 代码逻辑
monitorexit // 正常退出
monitorexit // 异常退出(保证释放锁)
method2():
ACC_SYNCHRONIZED // 方法访问标志
2.2 锁升级过程(JDK 1.6+优化)
面试必考:偏向锁 → 轻量级锁 → 重量级锁
java
public class LockUpgrade {
private static Object lock = new Object();
public static void main(String[] args) {
// 第一阶段:偏向锁(只有一个线程访问)
new Thread(() -> {
synchronized (lock) {
System.out.println("线程1首次获取锁 - 偏向锁");
}
}).start();
// 第二阶段:轻量级锁(多个线程交替访问)
new Thread(() -> {
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock) {
System.out.println("线程2竞争锁 - 轻量级锁");
}
}).start();
// 第三阶段:重量级锁(激烈竞争)
for (int i = 3; i <= 10; i++) {
final int id = i;
new Thread(() -> {
synchronized (lock) {
System.out.println("线程" + id + "竞争锁 - 重量级锁");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
}
}).start();
}
}
}
锁升级过程总结:
偏向锁:Mark Word记录线程ID(只有一个线程)
轻量级锁:CAS自旋尝试获取锁(少量竞争)
重量级锁:操作系统互斥量(激烈竞争)
2.3 synchronized的缺点
不可中断:线程会一直等待
非公平锁:可能导致线程饥饿
单一条件:无法实现复杂同步
三、volatile关键字
3.1 内存可见性
面试题:volatile如何保证可见性?
java
public class VisibilityDemo {
// 不加volatile,子线程可能看不到主线程的修改
private static /* volatile */ boolean flag = true;
public static void main(String[] args) throws Exception {
new Thread(() -> {
System.out.println("子线程启动");
while (flag) {
// 空循环
}
System.out.println("子线程结束");
}).start();
Thread.sleep(1000);
flag = false;
System.out.println("主线程修改flag为false");
}
}
加上volatile后的内存语义:
text
写操作:将工作内存的值刷新到主内存
读操作:从主内存读取最新值
3.2 禁止指令重排序
面试题:单例模式双重检查锁为什么要加volatile?
java
public class Singleton {
// 必须加volatile
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
// 不加volatile可能的执行顺序:
// 1. 分配内存空间
// 2. 引用赋值(instance不为null了)
// 3. 初始化对象 ← 可能重排序到后面
}
}
}
return instance;
}
}
四、AQS(AbstractQueuedSynchronizer)框架
4.1 AQS核心原理
面试题:AQS是如何实现同步的?
java
// AQS简化版实现思路
public abstract class SimpleAQS {
private volatile int state; // 同步状态
private Node head; // 等待队列头节点
private Node tail; // 等待队列尾节点
static final class Node {
Thread thread;
Node prev;
Node next;
int waitStatus; // 等待状态
}
// 子类需要实现的方法
protected boolean tryAcquire(int arg) { return false; }
protected boolean tryRelease(int arg) { return false; }
// 获取锁
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
// 获取失败,加入队列等待
Thread.currentThread().interrupt();
}
}
}
4.2 ReentrantLock实现分析
java
public class ReentrantLockDemo {
private static final ReentrantLock lock = new ReentrantLock(true); // 公平锁
public static void main(String[] args) {
// 1. 可重入性演示
lock.lock();
try {
System.out.println("第一次获取锁");
lock.lock(); // 可重入
try {
System.out.println("第二次获取锁(重入)");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
// 2. 尝试获取锁
if (lock.tryLock()) {
try {
System.out.println("获取锁成功");
} finally {
lock.unlock();
}
} else {
System.out.println("获取锁失败");
}
// 3. 可中断获取锁
Thread t = new Thread(() -> {
try {
lock.lockInterruptibly();
try {
Thread.sleep(5000);
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("被中断");
}
});
t.start();
try { Thread.sleep(1000); } catch (InterruptedException e) {}
t.interrupt(); // 中断等待
}
}
4.3 ReentrantLock vs synchronized
特性 synchronized ReentrantLock
实现机制 JVM内置 Java代码实现
锁类型 非公平锁 公平/非公平可选
可中断 ❌ 不支持 ✅ 支持
超时机制 ❌ 不支持 ✅ 支持
条件队列 单个 多个
性能 JDK6后优化相当 高度竞争时更好
五、线程池深度剖析
5.1 线程池参数详解
面试题:线程池各个参数的含义是什么?
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // corePoolSize: 核心线程数(长期存活的线程)
10, // maximumPoolSize: 最大线程数
60L, // keepAliveTime: 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // workQueue: 工作队列
Executors.defaultThreadFactory(), // threadFactory: 线程工厂
new ThreadPoolExecutor.AbortPolicy() // handler: 拒绝策略
);
5.2 线程池工作流程
text
-
提交任务
-
核心线程未满 → 创建新线程执行
-
核心线程已满 → 加入工作队列
-
队列已满且线程数未达最大 → 创建新线程执行
-
队列已满且线程数已达最大 → 执行拒绝策略
5.3 四种拒绝策略
java
public class RejectionPolicyDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 2, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2)
);// 1. AbortPolicy(默认):抛出异常 executor.setRejectedExecutionHandler( new ThreadPoolExecutor.AbortPolicy()); // 2. CallerRunsPolicy:调用者线程执行 // 3. DiscardPolicy:直接丢弃 // 4. DiscardOldestPolicy:丢弃队列最老任务 for (int i = 0; i < 10; i++) { final int taskId = i; try { executor.execute(() -> { System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId); try { Thread.sleep(1000); } catch (InterruptedException e) {} }); } catch (RejectedExecutionException e) { System.out.println("任务 " + taskId + " 被拒绝"); } } executor.shutdown();}
}
5.4 线程池最佳实践
java
public class ThreadPoolBestPractice {
// 1. 根据任务类型选择线程池
private static final ExecutorService ioPool = Executors.newCachedThreadPool();
private static final ExecutorService cpuPool = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);// 2. 自定义线程工厂
private static final ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder()
.setNameFormat(“business-thread-%d”)
.setDaemon(true)
.build();// 3. 监控线程池状态
public static void monitor(ThreadPoolExecutor executor) {
System.out.println("核心线程数: " + executor.getCorePoolSize());
System.out.println("活动线程数: " + executor.getActiveCount());
System.out.println("队列大小: " + executor.getQueue().size());
System.out.println("完成任务数: " + executor.getCompletedTaskCount());
}
}
六、并发集合类
6.1 ConcurrentHashMap实现原理
面试题:JDK 1.7和1.8的实现有什么区别?
java
public class ConcurrentHashMapDemo {
public static void main(String[] args) {
// JDK 1.8实现:数组 + 链表/红黑树 + CAS + synchronized
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 1. put操作:CAS + synchronized
map.put("key1", 1);
// 2. 线程安全的遍历
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 3. 原子操作
map.computeIfAbsent("key2", k -> 2);
map.computeIfPresent("key1", (k, v) -> v + 10);
// 4. 并发计数
LongAdder adder = new LongAdder(); // 比AtomicLong性能更好
adder.add(100);
System.out.println("计数: " + adder.sum());
}
}
6.2 CopyOnWriteArrayList适用场景
java
public class CopyOnWriteDemo {
private static final List list =
new CopyOnWriteArrayList<>(); // 读多写少场景
public static void main(String[] args) {
// 初始化数据
list.add("A");
list.add("B");
list.add("C");
// 多个线程同时读(不需要加锁)
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (String s : list) { // 快照迭代
System.out.println(Thread.currentThread().getName()
+ " 读取: " + s);
}
}).start();
}
// 写操作(复制新数组)
new Thread(() -> {
list.add("D"); // 创建新数组,不影响正在读的线程
System.out.println("添加新元素");
}).start();
}
}
七、JUC工具类实战
7.1 CountDownLatch(倒计时锁)
java
public class CountDownLatchDemo {
public static void main(String[] args) throws Exception {
// 场景:主线程等待所有子线程完成
CountDownLatch latch = new CountDownLatch(3);
for (int i = 1; i <= 3; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()
+ " 开始执行");
Thread.sleep((long) (Math.random() * 3000));
System.out.println(Thread.currentThread().getName()
+ " 执行完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 计数器减1
}
}, "线程-" + i).start();
}
System.out.println("主线程等待...");
latch.await(); // 阻塞直到计数器为0
System.out.println("所有线程执行完成,主线程继续");
}
}
7.2 CyclicBarrier(循环屏障)
java
public class CyclicBarrierDemo {
public static void main(String[] args) {
// 场景:多个线程相互等待,达到屏障点后继续
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println(“所有线程到达屏障,执行回调任务”);
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()
+ " 到达屏障前");
barrier.await(); // 等待其他线程
System.out.println(Thread.currentThread().getName()
+ " 通过屏障后");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
7.3 Semaphore(信号量)
java
public class SemaphoreDemo {
public static void main(String[] args) {
// 场景:控制同时访问资源的线程数
Semaphore semaphore = new Semaphore(3); // 允许3个线程同时访问
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 获取许可
System.out.println(Thread.currentThread().getName()
+ " 获得许可,开始执行");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()
+ " 执行完成,释放许可");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放许可
}
}, "线程-" + i).start();
}
}
}
7.4 CompletableFuture(异步编程)
java
public class CompletableFutureDemo {
public static void main(String[] args) throws Exception {
// 1. 异步执行
CompletableFuture future1 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return “Hello”;
});
// 2. 链式调用
CompletableFuture<String> future2 = future1.thenApplyAsync(s -> {
return s + " World";
});
// 3. 组合多个Future
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Java");
CompletableFuture<String> future4 = future2.thenCombine(future3,
(s1, s2) -> s1 + " " + s2);
// 4. 异常处理
CompletableFuture<String> future5 = future4.exceptionally(e -> {
System.out.println("发生异常: " + e.getMessage());
return "默认值";
});
System.out.println("最终结果: " + future5.get());
// 5. 所有任务完成
CompletableFuture<Void> all = CompletableFuture.allOf(
future1, future2, future3, future4);
all.join();
}
}
八、面试实战技巧
8.1 死锁问题分析
面试题:如何定位和解决死锁?
java
public class DeadLockDemo {
private static final Object lockA = new Object();
private static final Object lockB = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lockA) {
System.out.println("线程1获取lockA");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockB) {
System.out.println("线程1获取lockB");
}
}
}).start();
new Thread(() -> {
synchronized (lockB) {
System.out.println("线程2获取lockB");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockA) {
System.out.println("线程2获取lockA");
}
}
}).start();
}
}
死锁排查命令:
bash
1. 查看Java进程ID
jps -l
2. 查看线程堆栈
jstack
3. 查找死锁信息(Found one Java-level deadlock)
8.2 并发问题定位工具
jstack:线程堆栈分析
jconsole:图形化监控
VisualVM:性能分析
Arthas:在线诊断工具
📚 本章总结
知识点 掌握要点 面试权重
synchronized 实现原理、锁升级 ⭐⭐⭐⭐⭐
volatile 可见性、禁止重排序 ⭐⭐⭐⭐
AQS框架 实现原理、ReentrantLock ⭐⭐⭐⭐⭐
线程池 参数配置、拒绝策略 ⭐⭐⭐⭐⭐
JUC工具 CountDownLatch/CyclicBarrier等 ⭐⭐⭐⭐
🎯 下期预告
《Java面试通关指南(三):JVM与性能调优篇》
JVM内存模型深度解析
垃圾回收算法与实现
JVM性能监控工具
线上问题排查实战
💪 练习题
代码分析题:以下代码有什么问题?如何改进?
java
public class ThreadLocalLeak {
private static ThreadLocal<Map<String, Object>> threadLocal =
ThreadLocal.withInitial(HashMap::new);
public void setData(String key, Object value) {
threadLocal.get().put(key, value);
}
// 缺少remove操作,可能导致内存泄漏
}
设计题:如何实现一个生产者-消费者模型?
算法题:使用两个线程交替打印奇偶数
💡 面试小贴士
遇到并发问题:先分析是哪种并发问题(竞态条件、死锁、活锁等)
回答锁相关:从使用场景、性能、功能三个维度比较
线程池配置:结合具体业务场景(IO密集型 vs CPU密集型)
展现思考过程:即使不知道完整答案,也要展示分析思路
记住:面试官更看重你的思考过程,而不仅仅是正确答案!
本文为《Java面试通关指南》系列第二篇,后续将继续更新JVM、Spring、分布式等主题
关注作者,不错过最新干货!评论区留下你想了解的主题 🚀
更多推荐



所有评论(0)