一、痛点直击:生产环境最常见的并发问题
- 并发问题统计(基于 50 + 企业案例):
- 超卖 / 库存不一致(占比 38%)
- 数据重复提交(占比 25%)
- 线程池耗尽导致服务不可用(占比 20%)
- 死锁(占比 17%)
真实场景:某秒杀系统因未处理并发,导致超卖 1000 单,直接损失 5 万元
二、核心原理:Java 并发基础(JDK 8 通用)
- 线程安全的三大核心:
- 原子性:synchronized、AtomicInteger等原子类
- 可见性:volatile关键字(禁止指令重排序)
- 有序性:Happens-Before原则
- 并发工具对比(企业选型指南):
| 工具类 | 适用场景 | 优点 | 缺点 |
| synchronize | 简单并发场景(单服务) | 使用简单,JDK 优化好 | 不可中断,性能一般 |
| ReentrantLock | | 复杂并发场景(如超时) | 可中断,支持公平锁 | 需手动释放锁 |
| CountDownLatch | 等待多线程完成 | 轻量级,使用灵活 | 不可重复使用 |
| CyclicBarrier | 多线程协同工作 | 可重复使用 | 仅支持同一 JVM 内线程 |
| Semaphore | 限流场景 | 控制并发数精准 | 不支持分布式场景 |
| Redis 分布式锁 | 分布式系统并发控制 | 支持跨服务、跨机房 | 需处理锁超时、重入 |
|---|
三、实战案例:6 个生产级并发问题解决方案
案例 1:秒杀超卖问题(库存一致性)
-
问题代码(存在超卖):
java@Service public class SeckillService { @Autowired private ProductMapper productMapper; public boolean seckill(Long productId, Long userId) { // 1. 查询库存 Product product = productMapper.selectById(productId); if (product.getStock() { return false; } // 2. 扣减库存(并发时会超卖) productMapper.decreaseStock(productId); // 3. 创建订单 createOrder(productId, userId); return true; } } -
解决方案 1:数据库悲观锁(简单但性能低)
java// Mapper.xml中添加悲观锁 <select id="selectByIdForUpdate" resultType="Product"> SELECT * FROM product WHERE id = #{id} FOR UPDATE > // 服务层修改 public boolean seckill(Long productId, Long userId) { // 加悲观锁查询 Product product = productMapper.selectByIdForUpdate(productId); if (product.getStock() ) { return false; } productMapper.decreaseStock(productId); createOrder(productId, userId); return true; } -
解决方案 2:数据库乐观锁(高并发首选)
java// 实体类添加版本号字段 public class Product { private Long id; private Integer stock; private Integer version; // 版本号 // getter/setter } // Mapper.xml扣减库存时校验版本号 <update id="decreaseStockWithVersion"> UPDATE product SET stock = stock - 1, version = version + 1 WHERE id = #{id} AND version = #{version} AND stock > 0 // 服务层重试机制 public boolean seckill(Long productId, Long userId) { int retryCount = 3; // 重试3次 while (retryCount-- > 0) { Product product = productMapper.selectById(productId); if (product.getStock() return false; } // 乐观锁扣减库存 int rows = productMapper.decreaseStockWithVersion(productId, product.getVersion()); if (rows > 0) { // 扣减成功,创建订单 createOrder(productId, userId); return true; } // 扣减失败,重试 try { Thread.sleep(10); // 避免频繁重试 } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } return false; } -
解决方案 3:Redis 预扣库存(秒杀高并发场景)
java@Service public class SeckillService { @Autowired private StringRedisTemplate redisTemplate; @Autowired private ProductMapper productMapper; // 秒杀预热:将库存加载到Redis public void preloadStock(Long productId, Integer stock) { redisTemplate.opsForValue().set("seckill:stock:" + productId, stock.toString()); } public boolean seckill(Long productId, Long userId) { String stockKey = "seckill:stock:" + productId; String userKey = "seckill:user:" + productId + ":" + userId; // 1. 检查用户是否已抢购(避免重复下单) Boolean isExists = redisTemplate.hasKey(userKey); if (Boolean.TRUE.equals(isExists)) { return false; } // 2. Redis预扣库存(原子操作) Long stock = redisTemplate.opsForValue().decrement(stockKey); if (stock < 0) { // 库存不足,回滚 redisTemplate.opsForValue().increment(stockKey); return false; } // 3. 记录用户已抢购 redisTemplate.opsForValue().set(userKey, "1", 24, TimeUnit.HOURS); // 4. 异步同步到数据库(提高响应速度) CompletableFuture.runAsync(() -> { productMapper.decreaseStock(productId); createOrder(productId, userId); }); return true; } }
案例 2:线程池参数不合理导致服务雪崩
-
问题代码(默认线程池,高并发下耗尽):
java// 错误示例:无界队列+默认线程数(200) ExecutorService executor = Executors.newFixedThreadPool(200); -
企业级线程池配置(核心参数详解):
java@Configuration public class ThreadPoolConfig { @Bean public ExecutorService businessThreadPool() { // 核心参数计算:核心线程数 = CPU核心数 * 2 + 1 int corePoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1; int maxPoolSize = corePoolSize * 2; // 最大线程数 long keepAliveTime = 60; // 空闲线程存活时间(秒) // 有界队列(避免内存溢出) BlockingQueue<Runnable> queue = new ArrayBlockingQueue00); // 拒绝策略(避免任务丢失,使用调用者运行策略) RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); return new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, queue, new NamedThreadFactory("business-thread-"), handler ); } } -
线程池监控(集成 Spring Boot Actuator):
javascriptmanagement: endpoints: web: exposure: include: threadpool,health,infojava@Component @Endpoint(id = "threadpool") public class ThreadPoolEndpoint { @Autowired private ExecutorService businessThreadPool; @ReadOperation public Map getThreadPoolInfo() { ThreadPoolExecutor executor = (ThreadPoolExecutor) businessThreadPool; Map Object> info = new HashMap<>(); info.put("corePoolSize", executor.getCorePoolSize()); info.put("activeCount", executor.getActiveCount()); info.put("queueSize", executor.getQueue().size()); info.put("completedTaskCount", executor.getCompletedTaskCount()); info.put("rejectedCount", ((MyRejectedExecutionHandler) executor.getRejectedExecutionHandler()).getRejectedCount()); return info; } }
案例 3:分布式锁解决跨服务并发问题
-
Redis 分布式锁实现(Redisson):
java@Configuration public class RedissonConfig { @Bean public RedissonClient redissonClient() { Config config = new Config(); config.useSingleServer() .setAddress("redis://localhost:6379") .setPassword("123456") .setDatabase(0); return Redisson.create(config); } } @Service public class DistributeLockService { @Autowired private RedissonClient redissonClient; public > T executeWithLock(String lockKey, long leaseTime, TimeUnit unit, Supplier> supplier) { RLock lock = redissonClient.getLock(lockKey); try { // 尝试获取锁,最多等待3秒,锁定后30秒自动释放 boolean locked = lock.tryLock(3, leaseTime, unit); if (!locked) { throw new RuntimeException("获取锁失败,请稍后重试"); } // 执行业务逻辑 return supplier.get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("获取锁异常", e); } finally { // 释放锁(只有持有锁的线程才能释放) if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } } // 业务使用(跨服务更新库存) @Service public class InventoryService { @Autowired private DistributeLockService lockService; @Autowired private InventoryMapper inventoryMapper; public void updateInventory(Long productId, Integer num) { // 分布式锁key:产品ID(保证同一产品并发安全) String lockKey = "inventory:lock:" + productId; lockService.executeWithLock(lockKey, 30, TimeUnit.SECONDS, () -> { // 业务逻辑:查询库存→扣减库存 Inventory inventory = inventoryMapper.selectById(productId); if (inventory.getStock() throw new RuntimeException("库存不足"); } inventoryMapper.decreaseStock(productId, num); return null; }); } }
案例 4-6:其他高频并发问题
- 数据重复提交:基于 Redis + 令牌机制(前端请求时获取令牌,提交时校验)
- 死锁排查:`jstack 分析线程堆栈,避免嵌套锁、锁顺序不一致
- 并发集合使用:HashMap → ConcurrentHashMap,ArrayList → CopyOnWriteArrayList(读多写少场景)
四、并发编程避坑指南(生产环境必看)
- 避免使用ArrayList、HashMap等非线程安全集合
- 线程池避免使用Executors默认实现(无界队列易导致 OOM)
- synchronized锁粒度不宜过大(建议锁方法而非类)
- 分布式锁必须设置过期时间(避免死锁)
- 高并发场景下,优先使用乐观锁而非悲观锁
如果您想要获取更多专业知识以及实战能力,欢迎订阅《程序员实战避坑手册:从面试到职场的问题一站式解决》专栏,专栏内容包含Java 后端开发实战避坑指南。适配各阶段 Java 开发者,直击开发全链路高频痛点,囊括 IDEA/Git 配置、Docker 环境搭建、MyBatis-Plus/SpringBoot 性能优化、MySQL 调优及大厂面试技巧。专栏内容均为实战案例 + 避坑步骤 + 落地解决方案,帮你规避开发陷阱。