性能优化

线程池、伪共享和性能分析工具是并发编程性能优化的三大支柱,也是面试高频考点。

线程池原理 ⭐⭐⭐⭐⭐

为什么使用线程池

不使用线程池的问题

// 每次创建新线程
new Thread(() -> {
    // 执行任务
}).start();

// 问题:
// 1. 频繁创建/销毁线程,开销大
// 2. 无法控制线程数量,可能 OOM
// 3. 无法复用线程,资源浪费

线程池的优势

  • 降低资源消耗:线程复用

  • 提高响应速度:无需创建线程

  • 提高线程可管理性:统一分配、调优、监控

ThreadPoolExecutor 核心参数 ⭐⭐⭐⭐⭐

参数详解

参数
说明
推荐值

corePoolSize

核心线程数,即使空闲也不会销毁

CPU 密集型:CPU 核数+1 IO 密集型:2*CPU 核数

maximumPoolSize

最大线程数

根据业务场景设置

keepAliveTime

非核心线程空闲存活时间

30-60 秒

workQueue

任务队列

有界队列(防止 OOM)

handler

拒绝策略

根据业务选择

线程池执行流程

示例

工作队列选择

1. ArrayBlockingQueue(有界队列)

2. LinkedBlockingQueue(无界/有界队列)

3. SynchronousQueue(同步队列)

4. PriorityBlockingQueue(优先队列)

拒绝策略

1. AbortPolicy(默认)

2. CallerRunsPolicy

3. DiscardPolicy

4. DiscardOldestPolicy

自定义拒绝策略

线程池监控

线程池参数动态调整

线程池最佳实践

1. 参数设置原则

2. 使用有界队列

3. 给线程池命名

4. 优雅关闭线程池


伪共享和缓存行填充 ⭐⭐⭐⭐⭐

什么是伪共享

CPU 缓存层次

缓存行(Cache Line)

  • CPU 缓存以缓存行为单位,通常 64 字节

  • 读取变量时,会加载整个缓存行

伪共享问题

伪共享的危害

性能测试

缓存行填充方案

1. 手动填充(JDK 6/7)

2. 继承填充(Disruptor 方案)

3. @Contended 注解(JDK 8+)

什么时候使用缓存行填充

适用场景

  • 多线程频繁修改不同变量

  • 变量在内存中相邻

  • 性能要求极高

不适用场景

  • 单线程

  • 读多写少

  • 内存敏感(填充增加内存占用)

实战案例


性能分析工具 ⭐⭐⭐⭐

JDK 自带工具

1. jps(查看 Java 进程)

2. jstack(线程堆栈)

常见问题排查

3. jstat(GC 统计)

4. jmap(内存映像)

可视化工具

1. JConsole

2. VisualVM

3. JProfiler / YourKit

  • 商业工具,功能更强大

  • 支持远程监控

  • 内存泄漏检测

  • 热点方法分析

性能测试框架

JMH(Java Microbenchmark Harness)

性能优化检查清单

1. 线程池优化

2. 锁优化

3. 内存优化

4. 算法优化


面试要点 ⭐⭐⭐⭐⭐

Q1: 线程池的核心参数有哪些?

  • corePoolSize:核心线程数

  • maximumPoolSize:最大线程数

  • keepAliveTime:非核心线程存活时间

  • workQueue:工作队列

  • handler:拒绝策略

Q2: 线程池的执行流程?

  1. 核心线程未满 → 创建核心线程

  2. 核心线程已满 → 任务入队

  3. 队列已满 → 创建非核心线程

  4. 线程数达最大值 → 执行拒绝策略

Q3: 如何设置线程池参数?

  • CPU 密集型:核心线程数 = CPU 核数 + 1

  • IO 密集型:核心线程数 = 2 * CPU 核数

  • 混合型:线程数 = CPU 核数 * (1 + IO 耗时 / CPU 耗时)

Q4: 什么是伪共享?

  • CPU 缓存以缓存行(64 字节)为单位

  • 多个线程修改同一缓存行中的不同变量

  • 导致缓存行频繁失效,性能下降

Q5: 如何解决伪共享?

  • 手动填充:增加填充字段(56 字节)

  • @Contended 注解(JDK 8+)

  • 使变量独占缓存行

Q6: 常用的性能分析工具?

  • jstack:线程堆栈、死锁检测

  • jstat:GC 统计

  • jmap:堆内存快照

  • VisualVM:可视化监控

Q7: 线程池的拒绝策略有哪些?

  • AbortPolicy:抛异常(默认)

  • CallerRunsPolicy:调用者执行

  • DiscardPolicy:丢弃任务

  • DiscardOldestPolicy:丢弃最老任务

Q8: 如何优雅关闭线程池?


参考资料

  1. 书籍推荐:《Java 并发编程实战》、《Java 性能调优实战》

  2. JDK 文档:ThreadPoolExecutor、@Contended

  3. 工具文档:JMH、VisualVM

  4. 论文:《False Sharing》- Martin Thompson

Last updated