性能优化与最佳实践 (Performance Optimization & Best Practices)¶
掌握并发性能调优技巧,了解常见并发问题(死锁、活锁、饥饿)及解决方案,学习生产环境最佳实践
目录¶
1. 性能优化技巧 (Performance Optimization Tips)¶
1.1 减少锁的粒度¶
/**
* 锁粒度优化
* Lock Granularity Optimization
*/
public class LockGranularity {
// ❌ 不推荐:锁范围太大
public synchronized void process() {
doSomething1(); // 不需要同步
synchronized (this) {
count++; // 需要同步
}
doSomething2(); // 不需要同步
}
// ✅ 推荐:只锁必要的代码
public void process() {
doSomething1();
synchronized (this) {
count++; // 只锁必要的代码
}
doSomething2();
}
}
1.2 使用无锁数据结构¶
/**
* 无锁数据结构优化
* Lock-Free Data Structure Optimization
*/
public class LockFreeOptimization {
// ❌ 使用synchronized
private int count = 0;
public synchronized void increment() {
count++;
}
// ✅ 使用AtomicInteger(无锁)
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
// ✅ 高并发场景:使用LongAdder
private LongAdder highConcurrencyCount = new LongAdder();
public void increment() {
highConcurrencyCount.increment();
}
}
1.3 读写分离¶
/**
* 读写分离优化
* Read-Write Separation Optimization
*/
public class ReadWriteSeparation {
// ❌ 使用synchronized(读写都互斥)
private Map<String, String> cache = new HashMap<>();
public synchronized String get(String key) {
return cache.get(key);
}
public synchronized void put(String key, String value) {
cache.put(key, value);
}
// ✅ 使用ReadWriteLock(读可以并发)
private ReadWriteLock lock = new ReentrantReadWriteLock();
private Map<String, String> cache2 = new HashMap<>();
public String get(String key) {
lock.readLock().lock();
try {
return cache2.get(key);
} finally {
lock.readLock().unlock();
}
}
public void put(String key, String value) {
lock.writeLock().lock();
try {
cache2.put(key, value);
} finally {
lock.writeLock().unlock();
}
}
}
1.4 使用线程池¶
/**
* 线程池优化
* Thread Pool Optimization
*/
public class ThreadPoolOptimization {
// ❌ 直接创建线程(开销大)
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
doTask();
}).start();
}
// ✅ 使用线程池(资源复用)
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
doTask();
});
}
}
1.5 批量处理¶
/**
* 批量处理优化
* Batch Processing Optimization
*/
public class BatchProcessing {
// ❌ 逐个处理(锁竞争频繁)
public void processOneByOne(List<Item> items) {
for (Item item : items) {
synchronized (this) {
process(item);
}
}
}
// ✅ 批量处理(减少锁竞争)
public void processBatch(List<Item> items) {
List<Item> batch = new ArrayList<>();
synchronized (this) {
batch.addAll(items);
}
processBatch(batch); // 批量处理
}
}
2. 常见并发问题 (Common Concurrency Problems)¶
2.1 问题分类¶
| 问题类型 | 说明 | 严重程度 |
|---|---|---|
| 死锁(Deadlock) | 多个线程相互等待,无法继续 | ⭐⭐⭐⭐⭐ |
| 活锁(Livelock) | 线程不断重试,但无法取得进展 | ⭐⭐⭐⭐ |
| 饥饿(Starvation) | 线程长时间无法获得资源 | ⭐⭐⭐ |
| 竞态条件(Race Condition) | 结果依赖于执行顺序 | ⭐⭐⭐⭐ |
| 数据竞争(Data Race) | 多个线程同时访问共享数据 | ⭐⭐⭐⭐ |
2.2 问题示例¶
/**
* 常见并发问题示例
* Common Concurrency Problems Examples
*/
public class ConcurrencyProblems {
// 1. 死锁
private Object lock1 = new Object();
private Object lock2 = new Object();
public void deadlock() {
// 线程1:先获取lock1,再获取lock2
new Thread(() -> {
synchronized (lock1) {
synchronized (lock2) {
// ...
}
}
}).start();
// 线程2:先获取lock2,再获取lock1(相反顺序,可能死锁)
new Thread(() -> {
synchronized (lock2) {
synchronized (lock1) {
// ...
}
}
}).start();
}
// 2. 竞态条件
private int count = 0;
public void raceCondition() {
// 多个线程同时执行,结果不确定
new Thread(() -> count++).start();
new Thread(() -> count++).start();
// count的值不确定
}
}
3. 死锁问题 (Deadlock Problem)¶
3.1 死锁的条件¶
死锁发生的四个必要条件(缺一不可):
- 互斥条件 - 资源不能被多个线程同时使用
- 请求与保持 - 线程持有资源的同时请求其他资源
- 不可剥夺 - 资源不能被强制释放
- 循环等待 - 多个线程形成循环等待链
3.2 死锁示例¶
/**
* 死锁示例
* Deadlock Example
*/
public class DeadlockExample {
private Object lock1 = new Object();
private Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
System.out.println("线程1持有lock1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("线程1持有lock2");
}
}
}
public void method2() {
synchronized (lock2) {
System.out.println("线程2持有lock2");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("线程2持有lock1");
}
}
}
public static void main(String[] args) {
DeadlockExample demo = new DeadlockExample();
new Thread(demo::method1).start();
new Thread(demo::method2).start();
// 可能发生死锁
}
}
3.3 死锁检测¶
/**
* 死锁检测工具
* Deadlock Detection Tools
*/
public class DeadlockDetection {
public void detectDeadlock() {
// 使用jstack检测死锁
// jstack <pid> | grep -i deadlock
// 使用ThreadMXBean检测死锁
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = threadBean.findDeadlockedThreads();
if (deadlockedThreads != null) {
ThreadInfo[] threadInfos = threadBean.getThreadInfo(deadlockedThreads);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("死锁线程: " + threadInfo.getThreadName());
}
}
}
}
3.4 死锁预防和解决¶
/**
* 死锁预防和解决
* Deadlock Prevention and Solution
*/
public class DeadlockPrevention {
// 方法1:统一锁顺序
public void uniformLockOrder() {
// 所有线程按相同顺序获取锁
synchronized (lock1) {
synchronized (lock2) {
// ...
}
}
}
// 方法2:使用超时锁
public void timeoutLock() {
try {
if (lock1.tryLock(5, TimeUnit.SECONDS)) {
try {
if (lock2.tryLock(5, TimeUnit.SECONDS)) {
try {
// ...
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 方法3:避免嵌套锁
public void avoidNestedLocks() {
// 尽量减少锁的嵌套
// 使用更细粒度的锁
}
// 方法4:使用无锁数据结构
public void useLockFreeStructures() {
// 使用ConcurrentHashMap、AtomicInteger等
// 避免使用synchronized
}
}
4. 活锁和饥饿 (Livelock and Starvation)¶
4.1 活锁(Livelock)¶
活锁:线程不断重试,但无法取得进展。
/**
* 活锁示例
* Livelock Example
*/
public class LivelockExample {
private boolean flag = true;
public void livelock() {
// 线程1:不断重试
new Thread(() -> {
while (flag) {
if (tryAcquire()) {
flag = false;
} else {
Thread.yield(); // 让出CPU,但立即重试
}
}
}).start();
// 线程2:同样不断重试
new Thread(() -> {
while (flag) {
if (tryAcquire()) {
flag = false;
} else {
Thread.yield(); // 让出CPU,但立即重试
}
}
}).start();
// 两个线程不断重试,但都无法成功
}
}
解决方案: - 引入随机退避(Random Backoff) - 使用超时机制 - 重新设计算法
4.2 饥饿(Starvation)¶
饥饿:线程长时间无法获得资源。
/**
* 饥饿示例
* Starvation Example
*/
public class StarvationExample {
private ReentrantLock lock = new ReentrantLock(); // 非公平锁
public void starvation() {
// 高优先级线程总是先获得锁
Thread highPriorityThread = new Thread(() -> {
while (true) {
lock.lock();
try {
// 长时间持有锁
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
highPriorityThread.setPriority(Thread.MAX_PRIORITY);
highPriorityThread.start();
// 低优先级线程可能永远无法获得锁
Thread lowPriorityThread = new Thread(() -> {
lock.lock(); // 可能永远等待
try {
// ...
} finally {
lock.unlock();
}
});
lowPriorityThread.setPriority(Thread.MIN_PRIORITY);
lowPriorityThread.start();
}
}
解决方案: - 使用公平锁 - 合理设置线程优先级 - 避免长时间持有锁
5. 线程安全设计原则 (Thread Safety Design Principles)¶
5.1 原则1:不可变对象¶
/**
* 不可变对象:天然线程安全
* Immutable Objects: Naturally Thread-Safe
*/
public final class ImmutableConfig {
private final String key;
private final String value;
public ImmutableConfig(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
// 没有setter方法,对象不可变
}
5.2 原则2:线程封闭¶
/**
* 线程封闭:每个线程使用独立的对象
* Thread Confinement: Each Thread Uses Independent Objects
*/
public class ThreadConfinement {
// 使用ThreadLocal实现线程封闭
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
public void setValue(String value) {
threadLocal.set(value); // 每个线程独立的值
}
}
5.3 原则3:使用线程安全类¶
/**
* 使用线程安全类
* Use Thread-Safe Classes
*/
public class ThreadSafeClasses {
// ✅ 使用线程安全的集合
private ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
private CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// ✅ 使用原子类
private AtomicInteger counter = new AtomicInteger(0);
// ❌ 避免使用非线程安全的类
// private HashMap<String, String> unsafeMap = new HashMap<>();
}
5.4 原则4:同步最小化¶
/**
* 同步最小化:只同步必要的代码
* Minimize Synchronization: Only Synchronize Necessary Code
*/
public class MinimizeSynchronization {
// ✅ 只同步必要的代码块
public void process() {
doSomething1(); // 不需要同步
synchronized (this) {
criticalSection(); // 只同步关键部分
}
doSomething2(); // 不需要同步
}
}
6. 生产环境最佳实践 (Production Best Practices)¶
6.1 线程池配置¶
/**
* 生产环境线程池配置
* Production Thread Pool Configuration
*/
public class ProductionThreadPool {
// ✅ 推荐:自定义ThreadPoolExecutor
public ThreadPoolExecutor createThreadPool() {
int corePoolSize = Runtime.getRuntime().availableProcessors();
int maximumPoolSize = corePoolSize * 2;
return new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000), // 有界队列
new ThreadFactoryBuilder()
.setNameFormat("task-%d")
.setUncaughtExceptionHandler((t, e) -> {
logger.error("线程{}未捕获异常", t.getName(), e);
})
.build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
}
// ❌ 不推荐:使用Executors工具类(无界队列可能导致OOM)
// ExecutorService executor = Executors.newFixedThreadPool(10);
}
6.2 异常处理¶
/**
* 生产环境异常处理
* Production Exception Handling
*/
public class ProductionExceptionHandling {
// ✅ 设置未捕获异常处理器
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
logger.error("线程{}未捕获异常", t.getName(), e);
// 发送告警
alertService.sendAlert("线程异常", e);
});
// ✅ 线程池任务异常处理
executorService.submit(() -> {
try {
riskyOperation();
} catch (Exception e) {
logger.error("任务执行异常", e);
// 不要吞掉异常
}
});
}
6.3 资源清理¶
/**
* 生产环境资源清理
* Production Resource Cleanup
*/
public class ProductionResourceCleanup {
// ✅ 正确关闭线程池
public void shutdownThreadPool(ExecutorService executor) {
executor.shutdown(); // 停止接收新任务
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制关闭
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
logger.error("线程池未能正常关闭");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
// ✅ ThreadLocal清理
public void cleanupThreadLocal() {
try {
// 使用ThreadLocal
threadLocal.set(value);
} finally {
threadLocal.remove(); // 必须清理
}
}
}
6.4 监控和日志¶
/**
* 生产环境监控和日志
* Production Monitoring and Logging
*/
public class ProductionMonitoring {
// 线程池监控
@Scheduled(fixedRate = 60000)
public void monitorThreadPool() {
ThreadPoolExecutor executor = (ThreadPoolExecutor) executorService;
int queueSize = executor.getQueue().size();
int activeCount = executor.getActiveCount();
long completedTaskCount = executor.getCompletedTaskCount();
// 告警:队列积压
if (queueSize > 1000) {
logger.warn("线程池队列积压: {}", queueSize);
alertService.sendAlert("线程池队列积压", queueSize);
}
// 告警:线程池满载
if (activeCount == executor.getMaximumPoolSize()) {
logger.warn("线程池满载");
alertService.sendAlert("线程池满载");
}
}
// 死锁检测
@Scheduled(fixedRate = 300000) // 5分钟检测一次
public void detectDeadlock() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = threadBean.findDeadlockedThreads();
if (deadlockedThreads != null) {
logger.error("检测到死锁");
alertService.sendAlert("检测到死锁", deadlockedThreads);
}
}
}
7. 性能监控和调优 (Performance Monitoring and Tuning)¶
7.1 性能指标¶
/**
* 并发性能指标
* Concurrency Performance Metrics
*/
public class PerformanceMetrics {
// 线程池指标
public void threadPoolMetrics(ThreadPoolExecutor executor) {
// 核心线程数
int corePoolSize = executor.getCorePoolSize();
// 最大线程数
int maximumPoolSize = executor.getMaximumPoolSize();
// 当前线程数
int poolSize = executor.getPoolSize();
// 活跃线程数
int activeCount = executor.getActiveCount();
// 已完成任务数
long completedTaskCount = executor.getCompletedTaskCount();
// 总任务数
long taskCount = executor.getTaskCount();
// 队列大小
int queueSize = executor.getQueue().size();
}
// JVM线程指标
public void jvmThreadMetrics() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
// 线程总数
int threadCount = threadBean.getThreadCount();
// 峰值线程数
int peakThreadCount = threadBean.getPeakThreadCount();
// 死锁线程
long[] deadlockedThreads = threadBean.findDeadlockedThreads();
}
}
7.2 性能调优建议¶
- 线程池大小调优
- CPU密集型:线程数 = CPU核心数
-
IO密集型:线程数 = CPU核心数 * 2
-
减少锁竞争
- 使用无锁数据结构
- 减少锁的粒度
-
读写分离
-
批量处理
- 减少锁的获取次数
-
提高吞吐量
-
监控和告警
- 监控线程池状态
- 检测死锁
- 设置合理的告警阈值
8. 面试高频问题 (Interview Questions)¶
Q1: 如何避免死锁?¶
答案: 1. 统一锁顺序 2. 使用超时锁 3. 避免嵌套锁 4. 使用无锁数据结构
Q2: 死锁、活锁、饥饿的区别?¶
答案: - 死锁:线程相互等待,无法继续 - 活锁:线程不断重试,但无法取得进展 - 饥饿:线程长时间无法获得资源
Q3: 如何优化并发性能?¶
答案: 1. 减少锁的粒度 2. 使用无锁数据结构 3. 读写分离 4. 使用线程池 5. 批量处理
Q4: 生产环境如何配置线程池?¶
答案: - 使用有界队列 - 设置合理的拒绝策略 - 自定义线程工厂(命名、异常处理) - 监控线程池状态
Q5: 如何检测死锁?¶
答案: - 使用jstack工具 - 使用ThreadMXBean.findDeadlockedThreads() - 定期检测并告警
📖 扩展阅读¶
返回: 07-Java并发编程
上一章: 07-08 - 并发设计模式
返回目录: Java 语言基础导航 ←
恭喜完成Java并发编程学习! 🎉