首页 云计算

Java EE 多线程并发实战:从案例到性能优化(附源码)

分类:云计算
字数: (6958)
阅读: (3808)
内容摘要:Java EE 多线程并发实战:从案例到性能优化(附源码),

在 Java EE 项目中,多线程并发处理是提升系统性能的关键。本文将深入探讨**Java EE初阶启程记09---多线程案例(2)**涉及的常见问题,从底层原理、代码示例到实战经验,帮助开发者更好地理解和应用多线程技术。

问题场景重现:电商秒杀系统

假设我们正在开发一个电商秒杀系统。在高并发场景下,如果多个用户同时尝试抢购同一件商品,库存扣减和订单创建逻辑必须保证线程安全,否则会出现超卖、重复订单等问题。传统的同步锁 (synchronized) 在高并发下会成为性能瓶颈。我们需要更高效的并发控制方案。

Java EE 多线程并发实战:从案例到性能优化(附源码)

底层原理深度剖析:CAS 和原子类

为了避免synchronized的性能瓶颈,可以考虑使用CAS(Compare and Swap)操作和原子类。CAS是一种乐观锁机制,它尝试将内存位置的值更新为新值,前提是该位置的当前值与预期值匹配。原子类(如AtomicIntegerAtomicLong等)基于CAS实现,提供了线程安全的原子操作。

Java EE 多线程并发实战:从案例到性能优化(附源码)

代码解决方案:基于 AtomicInteger 的库存扣减

以下代码展示了如何使用 AtomicInteger 实现线程安全的库存扣减:

Java EE 多线程并发实战:从案例到性能优化(附源码)
import java.util.concurrent.atomic.AtomicInteger;

public class StockManager {
    private AtomicInteger stock = new AtomicInteger(100); // 初始库存 100

    public boolean decreaseStock() {
        int currentStock = stock.get();
        if (currentStock > 0) {
            // 尝试原子性地将库存减 1
            if (stock.compareAndSet(currentStock, currentStock - 1)) {
                System.out.println("库存扣减成功,剩余库存:" + stock.get());
                return true;
            } else {
                // CAS 操作失败,说明有其他线程已经修改了库存,重试
                return decreaseStock(); // 递归重试
            }
        } else {
            System.out.println("库存不足!");
            return false;
        }
    }

    public int getStock() {
        return stock.get();
    }

    public static void main(String[] args) throws InterruptedException {
        StockManager stockManager = new StockManager();
        // 模拟 10 个线程同时抢购
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                stockManager.decreaseStock();
            }).start();
        }

        Thread.sleep(2000); // 等待所有线程执行完毕
        System.out.println("最终库存:" + stockManager.getStock());
    }
}

结合 Redis 分布式锁解决超卖问题

单机环境下的 AtomicInteger 可以解决单个 JVM 进程内的线程安全问题。但在分布式环境中,我们需要使用分布式锁来保证多个 JVM 进程之间的线程安全。 Redis 分布式锁是一个常用的解决方案。

Java EE 多线程并发实战:从案例到性能优化(附源码)
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

public class RedisLock {

    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁的key
     * @param requestId 锁的value,区分不同请求
     * @param expireTime 超时时间,单位秒
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        SetParams setParams = SetParams.setParams().nx().ex(expireTime);
        String result = jedis.set(lockKey, requestId, setParams);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁的key
     * @param requestId 锁的value
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, 1, lockKey, requestId);

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
}

在秒杀业务代码中,先尝试获取 Redis 锁,成功后再进行库存扣减和订单创建。释放锁时,使用 Lua 脚本保证原子性。

实战避坑经验总结

  1. 锁的粒度:锁的粒度要适中。锁的范围太大,会降低并发度;锁的范围太小,又无法保证线程安全。
  2. 死锁:避免死锁。确保锁的释放,可以使用 try-finally 块或者 Redis 锁的过期时间。
  3. Redis 锁的续约:如果业务逻辑执行时间可能超过 Redis 锁的过期时间,需要考虑锁的续约机制,防止锁被意外释放。
  4. 性能监控:使用 Arthas 等工具对 Java EE 应用进行性能监控,分析多线程相关的性能瓶颈,并针对性地进行优化。例如,可以观察线程池的利用率、锁的竞争情况等。
  5. 选择合适的线程池:根据任务的类型(CPU 密集型、IO 密集型)选择合适的线程池大小和配置。不合理的线程池配置可能导致性能下降甚至系统崩溃。可以参考阿里巴巴 Java 开发手册中的建议。

通过理解 Java EE初阶启程记09---多线程案例(2) 背后的原理,并结合实际案例进行练习,可以提升Java EE应用的并发处理能力,构建高性能、高可用的系统。

Java EE 多线程并发实战:从案例到性能优化(附源码)

转载请注明出处: 代码一只喵

本文的链接地址: http://m.acea1.store/blog/760512.SHTML

本文最后 发布于2026-04-27 10:55:18,已经过了0天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 吃土少女 2 小时前
    求问,如果redis挂了,秒杀怎么处理?
  • 海带缠潜艇 14 小时前
    请问作者,Lua脚本释放锁那块,有什么需要注意的点吗?
  • 网瘾少年 6 天前
    写得真不错,秒杀那块受益匪浅,学到了Redis分布式锁的用法。
  • 土豆泥选手 2 天前
    请问作者,Lua脚本释放锁那块,有什么需要注意的点吗?
  • 酸辣粉 5 天前
    求问,如果redis挂了,秒杀怎么处理?