多并发时,用相应的锁方案保证库存不发生超卖。实现这个方案后,要有相应的场景测试方案。
很多时候,验证方案非常重要。
java可以利用CountDownLatch测试多并发。CountDownlatch通常用于某个业务线程需要等待其它多个线程完成某个条件后才进行相应操作。
CountDownLatch有countDown及await方法,分别对应减少计数器及当计数器大于0时阻塞当前线程。当计数量等于0时所有调用await方法且被阻塞的线程都被唤醒,然后进行相应操作。
此处用于模拟10个并发用户进行购买,库存初始化为5,看是否会发生超卖。
库存并发方案为对于某个sku的库存增减要求最终减去购买库存后库存量要大于等于0。这步由数据库语句完成。
例,库存更新语句:update skuStock set 更新后库存=原库存减去购买库存 where skuId=sku ID号 and 原库存减去购买库存后大于0
测试类:
public class StockTest implements ApplicationContextAware {
static {
System.setProperty("spring.profiles.active", "dev");
System.setProperty("configPath", "/home/zhao/work/config");
}
@Autowired
private SkuService skuService;
@Autowired
private SkuStockMapper skuStockMapper;
private static ApplicationContext applicationContext;
private static final CountDownLatch countDownLatch = new CountDownLatch(10);
private static final Logger logger = LoggerFactory.getLogger(StockTest.class);
@Test
public void testStock() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(20);
List<Future<Boolean>> futureList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
MockBuyTask mockBuyTask = new MockBuyTask();
futureList.add(executorService.submit(mockBuyTask));
}
int sucCount=0;
for(Future<Boolean> future:futureList){
if(future.get()){
sucCount++;
}
logger.info(future.get().toString());
}
logger.info("共"+sucCount+"人成功购物一件商品");
Assert.assertTrue(sucCount==5);
SkuStock skuStock = skuStockMapper.selectPoByPrimaryKey(7L);
Assert.assertTrue(skuStock != null && skuStock.getSkuId()==8L && skuStock.getCount() == 0);
}
static class MockBuyTask implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
countDownLatch.countDown();
logger.info("+wait for other man to run,thread:" + Thread.currentThread().getId());
countDownLatch.await();
SkuStock skuStock = new SkuStock();
skuStock.setSkuId(8L);
skuStock.setCount(1);
SkuStockMapper skuStockMapper = applicationContext.getBean(SkuStockMapper.class);
int rowCount = skuStockMapper.updateStockBySkuIdAndCountOfBuy(skuStock);
logger.info("+rowcount:" + rowCount);
return rowCount > 0 ? true : false;
}
}
@SuppressWarnings("static-access")
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext = applicationContext;
}
}