连载中 1/21

并发开篇:一行 i++,库存超卖 100 件

2026-10-23 · 79 阅读 · 0 评论 · 0 赞

库存超卖了 100 件

秒杀开始三分钟,客服电话被打爆:1000 件库存,卖出了 1100 件。复盘追到扣库存的代码,核心就一行:stock--。单测全过、压测全过、上线翻车——不是玄学,是并发编程的第一课:你以为的一行代码,在 CPU 眼里是一串动作,而线程随时可能在动作中间被插队。

一行代码的三个动作

把 stock-- 拆开看,CPU 实际执行的是三步:从内存读出 stock、在寄存器里减一、把结果写回内存。单线程没有任何问题;两个线程一交错就出事——线程 A 读到 1000 还没来得及写回,线程 B 也读到 1000,各自减一后写回 999:扣了两次,库存只少了 1。时间线画出来一目了然:

时刻    线程A               线程B               stock
t1     读 stock=1000                           1000
t2     寄存器减一           读 stock=1000      1000  ← B 读到旧值
t3                         寄存器减一         1000
t4     写回 999                                999   ← A 先落盘
t5                         写回 999           999   ← B 后落盘,覆盖 A

一组操作要么全部执行完、要么一步都不执行,这叫原子性。i++ 恰好不是原子操作——这就是超卖的元凶,也是并发三宗罪里的第一宗。

并发三宗罪:整个系列的地图骨架

库存超卖是第一宗罪——原子性。第二宗是可见性:线程 A 明明改了变量,线程 B 却可能永远看不到,因为它读的是自己 CPU 核心缓存里的旧副本。第三宗是有序性:代码的执行顺序未必和你写的一样,编译器和 CPU 都会重排指令,单线程下看不出来,多线程下就是偶现的灵异 Bug。记住这个对应关系,整个并发知识体系瞬间有了骨架:synchronized 和各类锁主要治原子性,volatile 主要治可见性,happens-before 规则给有序性兜底。本系列后面讲到的每一个工具,都会标注它治的是哪宗罪。

这个系列的路线图

二十一篇分五段:线程地基(创建方式、六种状态、生命周期);synchronized 与对象锁(字节码里的 monitorenter、锁升级、wait/notify);Java 内存模型(volatile、指令重排、happens-before、CAS 与原子类);AQS 与显式锁(ReentrantLock、读写锁、ThreadLocal);工程实战(线程池、ConcurrentHashMap、协作三件套、CompletableFuture、虚拟线程收官)。收官会放一张并发作战地图,按「治哪宗罪、用在什么场景」把所有工具串起来。

复现准备

JDK 17+,IDE 任选。所有示例都是 main 方法直接跑的裸代码,不依赖任何框架——并发是语言层的能力,框架只会干扰理解。把超卖 Demo 跑起来:

public class OversellDemo {
    static int stock = 1000;

    public static void main(String[] args) throws Exception {
        Runnable sell = () -> {
            for (int i = 0; i < 100; i++) {
                stock--;  // 三步:读、减、写
            }
        };
        for (int t = 0; t < 10; t++) {
            new Thread(sell).start();
        }
        Thread.sleep(1000);
        System.out.println("剩余库存:" + stock);  // 期望 0,实际每次都不一样
    }
}

多跑几次,剩余库存会飘出 999、992、983……每次都不同——并发问题的第一个特征就是概率性,不是必现,是看运气。而概率型 Bug 比必现型可怕十倍:你修好的,可能只是这次没触发而已。下一篇先把地基打牢:除了 new Thread 还有哪些正确的创建姿势,线程的六种状态又是怎么流转的。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Java#并发编程#线程安全#JUC#系列开篇

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10113 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9876 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9306 阅读 · 21 评论 · 287 赞