【027】阻塞队列玩透了?王二用 SynchronousQueue 优化秒杀系统,QPS 直接翻倍!

请添加图片描述

📙 作者: 编程技术圈(哇哥面试陪跑)
👉 欢迎关注、分享、评论
✔️ 持续分享更多干货内容
🌐🌏🌎➕tcmeta, 欢迎沟通交流

零、引入

“秒杀系统响应慢成龟速!QPS 才 500,用户全卡在下单页面!” 王二又愁眉苦脸地找哇哥,上次用 ArrayBlockingQueue 解决了订单丢单问题,但秒杀高峰期,订单处理还是慢得离谱,领导拍桌子要求 QPS 提到 1000,不然卷铺盖走人。

哇哥扫了眼王二的代码,乐了:“你用 ArrayBlockingQueue 存秒杀订单,相当于先把订单堆在队列里,再慢慢处理 —— 秒杀要的是‘即时处理’,得用 SynchronousQueue!这玩意儿没队列存储,订单直接从用户手里交到处理线程手里,响应快一倍!”

点赞 + 关注,跟着哇哥和王二,玩转阻塞队列的高级玩法,秒杀系统 QPS 翻倍,面试还能多拿 5k!

一、王二的秒杀坑:ArrayBlockingQueue 的 “存储瓶颈”

王二的秒杀系统用 ArrayBlockingQueue 做订单队列:生产者(用户下单)把订单放进队列,消费者(处理线程)从队列取。高峰期订单堆积在队列里,用户下单后要等半天才能看到结果,QPS 死活上不去。

🚀 王二的 “龟速” 秒杀代码

秒杀生产者:用户秒杀下单1
秒杀生产者:用户秒杀下单2
秒杀生产者:用户秒杀下单3
秒杀生产者:用户秒杀下单4
秒杀生产者:用户秒杀下单5
秒杀生产者:用户秒杀下单6
秒杀生产者:用户秒杀下单7
秒杀生产者:用户秒杀下单8
秒杀生产者:用户秒杀下单9
秒杀生产者:用户秒杀下单10
秒杀生产者:用户秒杀下单11
秒杀生产者:用户秒杀下单12
秒杀生产者:用户秒杀下单13
秒杀生产者:用户秒杀下单14
秒杀生产者:用户秒杀下单15

运行结果:订单堆积,处理慢如蜗牛

控制台里,生产者疯狂下单,消费者慢悠悠处理,订单堆积在队列里,1000 个订单要 20 秒才处理完,QPS 只有 50—— 这就是 “存储型队列” 的瓶颈:订单先存再处理,中间多了一道 “中转”,秒杀要的是 “即时性”,这道中转就是拖慢速度的元凶。

二、用 SynchronousQueue 改造:无存储,直接传递,QPS 翻倍

“SynchronousQueue 是个‘空队列’,没有任何存储容量,” 哇哥敲着代码解释,“生产者放订单(put)必须等消费者来取,消费者取订单(take)必须等生产者来放 —— 相当于订单直接从用户手里交到处理线程手里,没有中间堆积,响应自然快。”

‼️ 改造后的秒杀系统(QPS 翻倍)

请添加图片描述

package cn.tcmeta.blockingqueue;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;

/**
 * @author: laoren
 * @description: // 秒杀系统——SynchronousQueue版(QPS翻倍)
 * @version: 1.0.0
 */
public class SeckillSystemFast {

    // 核心:SynchronousQueue,无存储,订单直接传递
    static SynchronousQueue<Integer> orderQueue = new SynchronousQueue<>();
    // 消费者线程池:扩容到10个线程,应对秒杀流量
    static ExecutorService consumerPool = Executors.newFixedThreadPool(10);

    static class SeckillProducer implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 1000; i++) {
                try {
                    // put:必须等消费者取走订单才返回,无中间存储
                    orderQueue.put(i);
                    System.out.println(Thread.currentThread().getName() + ":用户秒杀下单" + i + "(直接交给处理线程)");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class SeckillConsumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    int orderId = orderQueue.take(); // 等生产者放订单
                    Thread.sleep(50); // 业务耗时不变
                    System.out.println(Thread.currentThread().getName() + ":处理秒杀订单" + orderId);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        // 启动10个消费者线程(比之前多,匹配秒杀流量)
        for (int i = 0; i < 10; i++) {
            consumerPool.submit(new SeckillConsumer());
        }

        // 启动生产者
        new Thread(new SeckillProducer(), "秒杀生产者").start();
        // 等待处理完成(只需10秒,比之前少一半)
        try {
            Thread.sleep(10000);
            consumerPool.shutdownNow();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("秒杀订单处理完成,QPS翻倍!");
    }

}

执行结果:

秒杀生产者:用户秒杀下单1000(直接交给处理线程)
pool-1-thread-5:处理秒杀订单991
pool-1-thread-3:处理秒杀订单992
pool-1-thread-1:处理秒杀订单993
pool-1-thread-7:处理秒杀订单995
pool-1-thread-10:处理秒杀订单994
pool-1-thread-6:处理秒杀订单996
pool-1-thread-9:处理秒杀订单997
pool-1-thread-8:处理秒杀订单999
pool-1-thread-2:处理秒杀订单998
pool-1-thread-4:处理秒杀订单1000
秒杀订单处理完成,QPS翻倍!

运行效果:10 秒处理完所有订单,QPS 破 1000
王二跑起来傻眼了:1000 个秒杀订单只用了 10 秒就处理完,QPS 直接涨到 1000+,用户下单后几乎秒出结果 —— 这就是 SynchronousQueue 的威力:无存储、直接传递,砍掉了 “订单中转” 的耗时,完美匹配秒杀的即时性需求。

三、阻塞队列的高级玩法:优先级、延时任务

解决了秒杀问题,哇哥又给王二讲了阻塞队列的另外两个常用玩法,都是工作中必用的。
请添加图片描述

🔰玩法 1:PriorityBlockingQueue——VIP 订单优先处理

“电商平台里,VIP 用户的订单要优先处理,普通用户的排后面,” 哇哥说,“PriorityBlockingQueue 能按优先级排序,完美解决这个需求。”

🎸 优先级订单代码示例

  • PriorityOrder
package cn.tcmeta.blockingqueue;

/**
 * @param priority 优先级:数字越大,优先级越高(1=普通,10=VIP)
 * @author: laoren
 * @description: // 订单类:实现Comparable,定义优先级
 * @version: 1.0.0
 */
// 订单类:实现Comparable,定义优先级
record PriorityOrder(int orderId, int priority) implements Comparable<PriorityOrder> {
    // 定义排序规则:高优先级先处理
    @Override
    public int compareTo(PriorityOrder o) {
        return Integer.compare(o.priority, this.priority);
    }

    @Override
    public String toString() {
        return "订单" + orderId + "(优先级:" + priority + ")";
    }
}
  • PriorityBlockingQueueSample
package cn.tcmeta.blockingqueue;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * @author: laoren
 * @description: // PriorityBlockingQueue实战:VIP订单优先
 * @version: 1.0.0
 */
public class PriorityBlockingQueueSample {
    static void main() throws InterruptedException {
        PriorityBlockingQueue<PriorityOrder> queue = new PriorityBlockingQueue<>();

        // 放入不同优先级的订单:普通、VIP、会员
        queue.put(new PriorityOrder(1, 1));   // 普通用户
        queue.put(new PriorityOrder(2, 10));  // VIP用户
        queue.put(new PriorityOrder(3, 5));   // 会员用户

        // 取出订单:按优先级排序
        System.out.println("开始处理订单(按优先级):");
        while (!queue.isEmpty()) {
            System.out.println("处理:" + queue.take());
        }
    }
}

运行结果:VIP 订单先处理
在这里插入图片描述

✅ 玩法 2:DelayQueue—— 订单超时未支付自动取消

“用户下单后 15 分钟没付款,要自动取消订单,” 哇哥说,“用 DelayQueue 实现这个需求,比 Timer、ScheduledExecutorService 更简单,还不会丢任务。”
请添加图片描述
延时订单代码示例

package cn.tcmeta.blockingqueue;

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * @param expireTime 订单过期时间
 */ // 延时订单:超时未支付自动取消
record DelayOrder(int orderId, long expireTime) implements Delayed {
    DelayOrder(int orderId, long expireTime) {
        this.orderId = orderId;
        // 延迟时间(毫秒)
        this.expireTime = System.currentTimeMillis() + expireTime;
    }

    // 剩余延迟时间(核心方法)
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    // 排序:先过期的订单先处理
    @Override
    public int compareTo(Delayed o) {
        return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
    }

    // 取消订单逻辑
    public void cancelOrder() {
        System.out.println("订单" + orderId + "超时未支付,自动取消!");
    }
}
  • DelayQueueSample
package cn.tcmeta.blockingqueue;

import java.util.concurrent.DelayQueue;

/**
 * @author: laoren
 * @date: 2025/12/7 14:35
 * @description: // DelayQueue实战:订单超时取消
 * @version: 1.0.0
 */
public class DelayQueueSample {

    public static void main(String[] args) throws InterruptedException {
        DelayQueue<DelayOrder> queue = new DelayQueue<>();

        // 放入3个延时订单:延迟2秒、1秒、3秒(模拟15分钟超时)
        queue.put(new DelayOrder(1, 2000));
        queue.put(new DelayOrder(2, 1000));
        queue.put(new DelayOrder(3, 3000));

        System.out.println("开始监控超时订单...");
        // 处理超时订单:take()会阻塞,直到有订单过期
        while (!queue.isEmpty()) {
            DelayOrder order = queue.take();
            order.cancelOrder();
        }
    }
}

运行结果:按过期时间取消订单

开始监控超时订单...
订单2超时未支付,自动取消!
订单1超时未支付,自动取消!
订单3超时未支付,自动取消!

四、阻塞队列选型指南:按场景选,不踩坑

哇哥给王二整理了一张 “选型表”,贴在显示器上,下次再也不用瞎选:
在这里插入图片描述
请添加图片描述

五、阻塞队列在并发编程中的核心应用

王二把哇哥的话总结成 “核心场景”,记在小本本上:

  • 削峰填谷:用 ArrayBlockingQueue/LinkedBlockingQueue 做普通任务队列,缓解高峰期压力;
  • 即时处理:用 SynchronousQueue 做秒杀、实时计算场景,提升 QPS;
  • 优先级处理:用 PriorityBlockingQueue 实现 VIP 优先、紧急任务优先;
  • 延时任务:用 DelayQueue 处理超时取消、定时提醒,替代 Timer;
  • 线程池:作为线程池的任务队列,控制任务堆积,避免 OOM。

✔️ 哇哥的彩蛋:当年的 “翻车经历”

“我早年做电商,用 Timer 处理订单超时,” 哇哥笑着说,“结果一个订单抛异常,整个 Timer 线程崩了,所有超时订单都没处理,亏了好几万 —— 后来换成 DelayQueue,每个订单一个线程处理,再也没出过问题。”

📌 最后说句实在的

请添加图片描述
阻塞队列不是 “花架子”,而是 Java 并发编程的 “刚需工具”—— 你之前踩的丢单、OOM、响应慢的坑,本质都是没选对队列。

记住:普通场景用 ArrayBlockingQueue,秒杀用 SynchronousQueue,优先级用 PriorityBlockingQueue,延时用 DelayQueue,按场景选,比瞎写代码靠谱 10 倍。

关注我,下次咱们扒一扒阻塞队列的底层实现(ReentrantLock+Condition),让你不仅会用,还能讲透原理,面试时直接碾压面试官!

请添加图片描述
请添加图片描述
请添加图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值