考慮情況:java是個多線程機制。多線程是一把雙刃劍,一旦設計到多個線程操作共享資源的情況下,處理不好就會出現線程安全問題。線程安全性可能是非常複雜的,在沒有充足的同步情況下,多個線程執行順序是不好操作的。
同步和異步: 同步就是一件事,一件事情一件事的做。異步就是,做一件事情,不引響做其他事情。
應用場景:用戶下訂單的時候考慮到併發情況。商品購買是有庫存的,一個用戶可以購買商品數量爲多個,如多個用戶同時下單可能會產生商品不夠,但訂單生成成功問題。
解決辦法:採用樂觀鎖解決,商品表中添加一個凍結數量字段和一個數據版本字段
下訂單之前先獲取當前商品信息。
加鎖:
// 查詢當前商品信息
// 【樂觀鎖】獲取版本
ULCommodityTg dbCommodityTg = ulCommodityTGDao.selectById(orderSubmitPO.getCommodityId());
if (dbCommodityTg == null) {
bo.setOrderInfo(ULApiResultTips.Order.ORDER_ERROR);
return new ULResult<CResultOrderInfoBO>(bo);
}
boolean flag = false;
for (int i = 0; i < 3; i++) {
// 使用當前數據版本,追加凍結數量
Integer happyLock = ulCommodityTGDao.updateByHappyLock(dbCommodityTg.getId(),
dbCommodityTg.getDataVersion(), orderSubmitPO.getQuantity());
if (happyLock > 0) {
flag = true;
break;
}
Thread.sleep(1000);
// 重新獲取版本,再次嘗試下單
dbCommodityTg = ulCommodityTGDao.selectById(orderSubmitPO.getCommodityId());
}
// 數據版本已增加,當前訂單更新失敗
if (!flag) {
bo.setOrderInfo(ULApiResultTips.Order.ORDER_OVERFLOW);
return new ULResult<CResultOrderInfoBO>(bo);
}
根據版本號判斷當前下單用戶。
解鎖:
ULShop dbShop = ulShopDao.selectById(dbOrder.getShopId());
// 商鋪銷售量加一
dbShop.setSalesVolume(dbShop.getSalesVolume() + 1);
dbShop.setUpdateTime(now);
ulShopDao.updateByPrimaryKeySelective(dbShop);
// 團購 美食
if (ULOrderJumpTypeEmun.TG_FOOD.getType().equals(dbOrder.getJumpType())) {
// 加銷量、減凍結數量
List<ULOrderFood> dbFoods = ulOrderFoodDao.selectByProperty("orderNo", orderNo);
ULCommodityTg dbCommodity = ulCommodityTGDao.selectById(dbFoods.get(0).getCommodityId());
dbCommodity.setSalesFreeze(dbCommodity.getSalesFreeze() - dbOrder.getQuantity());
dbCommodity.setSalesQuantity(dbCommodity.getSalesQuantity() + dbOrder.getQuantity());
// 添加銷量
dbCommodity.setSalesQuantity(dbCommodity.getSalesQuantity() + dbFoods.size());
dbCommodity.setUpdateTime(now);
ulCommodityTGDao.updateByPrimaryKeySelective(dbCommodity);
}
至此:樂觀鎖添加完成,保證了用戶購買的數量不能超過當前庫存。