java防并发处理思路:
这个并发问题困扰小杰好久了,主要是 锁能锁住,但是spring的session没有及时更新,导致下面的进程进来时,还是查不到上次更新后的数据。
解决方案:
1、加上ReentrantLock锁
2、检查订单的数据,double check下 是否上次已经执行过了。在sql的最后加上 for update
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
下面是加锁代码: /** * 返回根据产品和设施所加的锁 * * @param facilityId * 设施ID * @param productId * 产品ID * * @return ReentrantLock */ private ReentrantLock getInventoryTransactionLock(String facilityId, String productId) { String key = facilityId + "_" + productId; try { java.security.MessageDigest md = java.security.MessageDigest .getInstance("MD5"); md.update(key.getBytes()); byte[] result = md.digest(); key = new String(result, 0, 16); } catch (java.security.NoSuchAlgorithmException e) { key = facilityId + "_" + productId; } ReentrantLock lock = locks.get(key); if (lock == null) { synchronized (_lock) { lock = locks.get(key); if (lock == null) { lock = new ReentrantLock(); locks.put(key, lock); } } } return lock; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 加锁 Lock lock = this.getInventoryTransactionLock(fromFacilityId, productId); lock.lock(); // double check 代码 public boolean checkOrderProductOutNumber(Session session, String orderGoodsId, String productId, BigDecimal amount) { String sql = "select og.goods_number,ifnull(sum(-iid.quantity_on_hand_diff),0) as out_num" + " from ecshop.ecs_order_goods og " + " left join romeo.product_mapping pm ON og.goods_id = pm.ecs_goods_id and og.style_id = pm.ecs_style_id" + " left join romeo.inventory_item_detail iid ON convert(og.rec_id using utf8) = iid.order_goods_id" + " left join romeo.inventory_item ii ON ii.inventory_item_id = iid.inventory_item_id" + " where og.rec_id = '"+orderGoodsId+"' and pm.product_id = '"+productId+"'" + " group by og.rec_id for update"; 最后关闭锁 if (lock != null) { lock.unlock(); } |
大功搞定,困恼了小杰2年的问题,终于搞定了,之前一直想着怎样让session更新,却忘了mysql自带的for update 功能。
希望能帮助到需要的人。