Here is a simple logic simulation spike, stock and orders for the two Map, simulate inventory list and order form
public void orderProductMockDiffUser (String productId) { // 1. The merchandise inventory query, is 0 end of the event. int stockNum = stock.get (the productId); IF (stockNum == 0 ) { the throw new new SellException (100, "end of the event" ); } the else { // 2. Order (to simulate different users different openid) orders.put ( KeyUtil.genUniqueKey (), the productId); // 3. Save stock (analog memory (or Redis) Save the stock) stockNum =. 1-stockNum ; the try { // 4. mimics some IO operations or other business Thread.sleep ( 100); } catch (InterruptedException e) { e.printStackTrace(); } stock.put(productId,stockNum); } }
This logic when there is a problem complicated by the large amount of time, the number of inconsistencies number of goods sold and inventory will result subtracted
We can use the synchronized keyword to solve this problem, plus synchronized on the method name
public synchronized void orderProductMockDiffUser(String productId)
While the number of synchronized can solve the problem of inconsistency, but the disadvantages are also obvious, and that is slow, because the synchronized modification of the method is synchronous, meaning that only one thread access to this method, and synchronized applies only to a single point.
A better approach is to use distributed lock redis
@Component @ SLF4J public class RedisLock { @Autowired Private StringRedisTemplate redisTemplate; / ** * lock * @param Key Product ID * @param value of the current time + timeout * @return * / public Boolean Lock (String Key, String value) { // setIfAbsent () is the redis setnx, set value when the key does not exist iF {(redisTemplate.opsForValue () setIfAbsent (key, value).) // locked successfully return to true ; } // when the lock already exists you can acquire the lock of value, to determine whether expired CurrentValue = String redisTemplate.opsForValue () GET (Key);. // If the lock expires IF (! StringUtils.isEmpty (currentValue) && Long.parseLong (currentValue) < System.currentTimeMillis ()) { // obtaining a lock time String oldValue = redisTemplate.opsForValue () getAndSet (Key, value);. // if two threads enter here, may be determined by equality oldValue plurality of threads to limit the currentValue locking IF (StringUtils.isEmpty (! oldValue) && oldValue.equals (currentValue)) { return to true ; } } return to false ; } /** * 解锁 * @param key * @param value */ public void unlock(String key, String value) { try { String currentValue = redisTemplate.opsForValue().get(key); if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) { redisTemplate.opsForValue().getOperations().delete(key); } }catch (Exception e) { log.error("【redis分布式锁】解锁异常, {}", e); } } }
So long as we add locks to spike when the logical beginning, the end of the logic unlock it. redis than synchronized distributed lock not only faster, but also for distributed.
public void orderProductMockDiffUser (String the productId) { // lock Long Time = System.currentTimeMillis () + TIMEOUT; IF (! redisLock.lock (the productId, String.valueOf (Time))) { the throw new new SellException (ResultEnum.REDIS_LOCK_FAIL); } // 1. query the merchandise inventory for 0 end of the event. int stockNum = stock.get (the productId); IF (stockNum == 0 ) { the throw new new SellException (100, "end of the event" ); } the else { // 2. Order (to simulate different users different openid) orders.put (KeyUtil.genUniqueKey (), the productId); // 3. Save stock (analog memory (or Redis) Save the stock) stockNum =. 1-stockNum ; the try { // 4. mimics some other business operations or IO thread.sleep (100 ); } the catch (InterruptedException E) { e.printStackTrace (); } stock.put (the productId, stockNum); } // unlock redisLock.unlock (the productId, String.valueOf (Time)); }