跳转至

性能优化与最佳实践 (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 死锁的条件

死锁发生的四个必要条件(缺一不可):

  1. 互斥条件 - 资源不能被多个线程同时使用
  2. 请求与保持 - 线程持有资源的同时请求其他资源
  3. 不可剥夺 - 资源不能被强制释放
  4. 循环等待 - 多个线程形成循环等待链

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 性能调优建议

  1. 线程池大小调优
  2. CPU密集型:线程数 = CPU核心数
  3. IO密集型:线程数 = CPU核心数 * 2

  4. 减少锁竞争

  5. 使用无锁数据结构
  6. 减少锁的粒度
  7. 读写分离

  8. 批量处理

  9. 减少锁的获取次数
  10. 提高吞吐量

  11. 监控和告警

  12. 监控线程池状态
  13. 检测死锁
  14. 设置合理的告警阈值

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并发编程学习! 🎉