Java多线程实战避坑指南:从入门到生产实践

在微服务架构下,多线程编程已经成为Java开发者的必备技能。本文将帮助你掌握多线程开发的核心知识,避开常见陷阱。

一、为什么要深入理解多线程?

1.1 现实问题

  • 接口响应慢
  • CPU利用率低
  • 内存泄漏频发
  • 死锁难以排查
  • 并发BUG难复现

1.2 业务场景

  • 批量数据处理
  • 并行任务执行
  • 异步消息处理
  • 定时任务调度
  • 实时数据计算

二、线程池的正确使用

2.1 常见错误示例

// ❌ 错误示例:直接使用Executors创建线程池
ExecutorService executor = Executors.newFixedThreadPool(10);

// ❌ 错误示例:没有明确的拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    10, 10, 0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<>()
);

2.2 最佳实践

// ✅ 推荐示例:自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                      // 核心线程数
    10,                     // 最大线程数
    60L,                    // 空闲线程存活时间
    TimeUnit.SECONDS,       // 时间单位
    new LinkedBlockingQueue<>(100),  // 有界队列
    new ThreadFactoryBuilder()
        .setNameFormat("业务名称-thread-%d")
        .build(),          // 自定义线程工厂
    new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝策略
);

// 优雅关闭线程池
void shutdownThreadPool(ThreadPoolExecutor executor) {
    executor.shutdown();
    try {
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
            executor.shutdownNow();
        }
    } catch (InterruptedException e) {
        executor.shutdownNow();
    }
}

三、并发集合实战

3.1 Map的线程安全

// ❌ 错误示例:在并发环境下使用HashMap
Map<String, Object> map = new HashMap<>();

// ✅ 推荐示例1:ConcurrentHashMap(推荐)
Map<String, Object> map = new ConcurrentHashMap<>();

// ✅ 推荐示例2:需要独占锁时使用Collections.synchronizedMap
Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());

3.2 List的线程安全

// ❌ 错误示例:ArrayList非线程安全
List<String> list = new ArrayList<>();

// ✅ 推荐示例1:写多读少场景
List<String> list = new CopyOnWriteArrayList<>();

// ✅ 推荐示例2:读多写少场景
List<String> list = Collections.synchronizedList(new ArrayList<>());

四、锁的最佳实践

4.1 避免死锁

// ❌ 错误示例:容易造成死锁
synchronized void transfer(Account from, Account to, double amount) {
    synchronized(from) {
        synchronized(to) {
            // 转账逻辑
        }
    }
}

// ✅ 推荐示例:按固定顺序获取锁
synchronized void transfer(Account from, Account to, double amount) {
    Account first = from.getId() < to.getId() ? from : to;
    Account second = from.getId() < to.getId() ? to : from;
    synchronized(first) {
        synchronized(second) {
            // 转账逻辑
        }
    }
}

4.2 锁的选择

// ✅ 推荐示例1:细粒度锁
public class UserService {
    private final Map<Long, Lock> lockMap = new ConcurrentHashMap<>();
    
    public void updateUser(Long userId) {
        Lock lock = lockMap.computeIfAbsent(userId, k -> new ReentrantLock());
        lock.lock();
        try {
            // 更新用户逻辑
        } finally {
            lock.unlock();
        }
    }
}

// ✅ 推荐示例2:读写锁
public class CacheService {
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Map<String, Object> cache = new HashMap<>();
    
    public Object read(String key) {
        rwLock.readLock().lock();
        try {
            return cache.get(key);
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    public void write(String key, Object value) {
        rwLock.writeLock().lock();
        try {
            cache.put(key, value);
        } finally {
            rwLock.writeLock().unlock();
        }
    }
}

五、实战案例:高性能缓存

5.1 并发缓存实现

public class ConcurrentCache<K, V> {
    private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
    private final LoadingCache<K, V> guavaCache;
    
    public ConcurrentCache(CacheLoader<K, V> loader) {
        this.guavaCache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(loader);
    }
    
    public V get(K key) {
        V value = cache.get(key);
        if (value == null) {
            // 双重检查锁定
            synchronized(cache) {
                value = cache.get(key);
                if (value == null) {
                    value = guavaCache.getUnchecked(key);
                    cache.put(key, value);
                }
            }
        }
        return value;
    }
}

六、性能优化技巧

6.1 避免过度同步

// ❌ 错误示例:方法级同步
public synchronized void process(List<Order> orders) {
    orders.forEach(this::processOrder);
}

// ✅ 推荐示例:缩小同步范围
public void process(List<Order> orders) {
    orders.parallelStream()
          .forEach(order -> {
              synchronized(order) {
                  processOrder(order);
              }
          });
}

6.2 合理使用volatile

// ✅ 推荐示例:状态标志
public class TaskManager {
    private volatile boolean running = true;
    
    public void shutdown() {
        running = false;
    }
    
    public void process() {
        while (running) {
            // 处理任务
        }
    }
}

七、实际问题解决

7.1 常见问题

  1. CPU飙升

    • 使用jstack查看线程状态
    • 分析线程池配置
    • 检查死循环代码
  2. 内存泄漏

    • 使用JProfiler分析
    • 检查ThreadLocal使用
    • 排查集合类使用
  3. 响应超时

    • 分析线程池队列
    • 检查锁的争用
    • 优化执行逻辑

7.2 监控方案

// 线程池监控
public class MonitoredThreadPool extends ThreadPoolExecutor {
    private final AtomicLong totalTasks = new AtomicLong(0);
    private final AtomicLong completedTasks = new AtomicLong(0);
    
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        totalTasks.incrementAndGet();
    }
    
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        completedTasks.incrementAndGet();
        // 记录监控指标
        if (t != null) {
            log.error("Task execution failed", t);
        }
    }
}

八、最佳实践总结

8.1 线程池使用

  • 根据业务场景选择核心参数
  • 使用有界队列避免OOM
  • 自定义线程工厂便于排查
  • 优雅关闭避免任务丢失

8.2 并发工具选择

  • 优先使用并发集合
  • 合理使用锁机制
  • 注意锁的粒度
  • 考虑性能影响

8.3 代码优化

  • 缩小同步范围
  • 避免嵌套锁
  • 合理使用volatile
  • 注意线程安全性

总结

多线程开发要点:

  1. 正确使用线程池
  2. 选择合适的并发工具
  3. 注意锁的使用方式
  4. 做好监控和优化

建议:

  • 从实际问题出发
  • 循序渐进地优化
  • 重视性能监控
  • 保持代码简洁

你可能感兴趣的:(Java进阶之路,java,windows,python,多线程)