Mastering High‑Concurrency Flash‑Sale: 7 Locking & Queue Strategies in SpringBoot

This article analyzes a high‑concurrency flash‑sale scenario using SpringBoot, MySQL, and JMeter, demonstrates seven implementations—from service‑level locks to AOP, pessimistic/optimistic locks, and queue‑based designs—examines their trade‑offs with concrete code, test results, and practical recommendations.

Architect
Architect
Architect
Mastering High‑Concurrency Flash‑Sale: 7 Locking & Queue Strategies in SpringBoot

High‑concurrency situations are common in internet companies; this article simulates such a scenario with a flash‑sale (seckill) of a product to explore various concurrency‑control techniques.

Test Environment

SpringBoot 2.5.7, MySQL 8.0, MybatisPlus, Swagger 2.9.2

Load‑testing tool: JMeter

Scenario: Decrease stock → Create order → Simulate payment

1. Initial Implementation – Service‑Level Lock

The controller calls secondKillService.startSecondKillByLock. The service method is annotated with @Transactional and surrounds the business logic with a ReentrantLock. The code checks stock, decrements it, creates an order, and inserts a payment record.

Although the lock seems correct, testing 1,000 concurrent requests for 100 items shows overselling because the lock is released in the finally block before the transaction commits, allowing other threads to read stale stock.

Oversell test result
Oversell test result

2. Solving Oversell – Adjusting Lock Timing

The core problem is that the lock is released before the transaction finishes. Two main ways to fix it are moving the lock to the controller layer or applying it via AOP before the transactional method executes.

2.1 Approach 1 – Lock in Controller

@PostMapping("/start/lock")
public Result startLock(long skgId){
    lock.lock();
    try{
        // call service
    }finally{
        lock.unlock();
    }
    return Result.ok();
}

Pressure tests with different concurrency/stock ratios (1000/100, 1000/1000, 2000/1000) show that when requests exceed stock, no under‑sale occurs, but when requests are less than or equal to stock, occasional under‑sale appears, which is expected.

Lock‑in‑controller test
Lock‑in‑controller test

2.2 Approach 2 – AOP Lock

A custom annotation @ServiceLock is defined and an aspect LockAspect intercepts methods annotated with it, acquiring a ReentrantLock before proceeding and releasing it afterward.

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceLock {}

@Aspect
public class LockAspect{
    private static final Lock lock = new ReentrantLock(true);
    @Pointcut("@annotation(com.scorpios.secondkill.aop.ServiceLock)")
    public void lockAspect(){}
    @Around("lockAspect()")
    public Object around(ProceedingJoinPoint jp) throws Throwable{
        lock.lock();
        try{ return jp.proceed(); }
        finally{ lock.unlock(); }
    }
}

The service method is then annotated with @ServiceLock and retains the original transactional logic. This keeps the lock active for the whole transaction, eliminating oversell.

2.3 Approach 3 – Pessimistic Lock (FOR UPDATE)

The DAO method uses SELECT * FROM seckill WHERE seckill_id=#{skgId} FOR UPDATE to acquire a row‑level lock. The service method runs inside a transaction, reads the locked row, updates stock, creates order and payment.

2.4 Approach 4 – Pessimistic Lock via UPDATE

An

UPDATE seckill SET number=number-1 WHERE seckill_id=#{skgId} AND number>0

statement directly decrements stock while holding a table lock. After a successful update, order and payment records are inserted.

2.5 Approach 5 – Optimistic Lock

A version column is added. The update statement checks the version and increments it atomically:

UPDATE seckill SET number=number-#{number}, version=version+1
WHERE seckill_id=#{skgId} AND version=#{version}

If the update count is zero, the operation fails. Tests show many update‑conflict exceptions and under‑sale when request count is close to stock, so this method is not recommended.

2.6 Approach 6 – Blocking Queue

A singleton SecondKillQueue (capacity 100) stores SuccessKilled objects. Producer threads enqueue requests; a consumer thread (implemented via ApplicationRunner) dequeues and calls the AOP‑locked service method. Important notes:

Both startSecondKillByAop and startSecondKillByLock produce identical results when called from the consumer.

If queue length equals product quantity, under‑sale may appear; increasing the queue size mitigates this.

Blocking‑queue test
Blocking‑queue test

2.7 Approach 7 – Disruptor Queue

Disruptor provides a high‑performance ring‑buffer. An event factory creates SecondKillEvent objects, a translator publishes them, and a consumer handler invokes the AOP‑locked service method. Performance tests show ~600 k orders/second on a single thread, but oversell still occurs due to the time gap between enqueue and dequeue.

Note: In both the business layer and AOP method, do not throw exceptions (e.g., throw new RuntimeException() ) because an uncaught exception will terminate the consumer thread.

3. Summary of Findings

Approaches 1 and 2 (service‑level lock and controller‑level lock) solve the oversell problem by ensuring the lock lives longer than the transaction.

Approaches 3, 4, and 5 rely on database locks; the pessimistic row‑lock (FOR UPDATE) and table‑lock (UPDATE) work well, while the optimistic‑lock version performs the worst.

Approaches 6 and 7 (queue‑based) decouple request intake from processing; they require careful exception handling and may cause under‑sale when the queue length matches stock.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

databaseconcurrencyJMeterSpringBootLockQueueSeckill
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.