当我们在单机情况下,遇到并发问题,可以使用juc包下的lock锁,或者synchronized关键字来加锁。但是这俩都是JVM级别的锁,如果跨了JVM这两个锁就不能控制并发问题了,也就是说在分布式集群环境中,需要寻求其他方法来解决并发问题。前面也说到可以使用redis的setnx操作,如果不存在则set,如果存在则不set。也就是说每个服务实例都对同一个key进行操作。谁能set成功就认为获取到了锁。可以执行下面的操作。执行完之后释放锁。如下按照上述逻辑来简单实现一个分布式锁:
package com.nijunyang.redis.lock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * Description: * Created by nijunyang on 2020/3/17 23:53 */ @RestController public class LockController { @Autowired ValueOperations<String, Object> valueOperations; @Autowired RedisTemplate<String, Object> redisTemplate; String lock = "lock"; String quantityKey = "quantity"; @GetMapping("/deduct-stock") public String deductStock() { try { boolean getLock = valueOperations.setIfAbsent(lock, 1); if (!getLock) { return "没有获取到锁"; } //使用当做数据库,只是模拟扣减库存场景,因此不使用原子操作 Integer quantity = (Integer) valueOperations.get(quantityKey); if (quantity > 0) { --quantity; valueOperations.set(quantityKey, quantity); System.out.println("扣减库存成功,剩余库存: " + quantity); } else { System.out.println("扣减库存成功,剩余库存: " + quantity); } return "true"; } finally { redisTemplate.delete(lock); } } }
如果不出意外这个锁是可以用的,但是如果拿到锁之后,在执行业务的过程中,服务挂了,就会导致锁没有释放,其他服务永远无法拿到锁,因此我们可以优化一下,加锁的同时给锁设置一个过期时间,这样来保证,拿到锁在执行业务的时候挂了,到了过期时间之后,其他服务一样可以继续获取锁。
package com.nijun