一、Redis分布式锁概念篇
建议直接采用Redis的官方推荐的Redisson作为redis的分布式锁
1.1、为什么要使用分布式锁
我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug!
注意这是单机应用,也就是所有的请求都会分配到当前服务器的JVM内部,然后映射为操作系统的线程进行处理!而这个共享变量只是在这个JVM内部的一块内存空间!
话不多说,直接进入实战!
二、Redis分布式锁实战篇
2.1、导入依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-redis</artifactId>
- <version>1.4.7.RELEASE</version>
- </dependency>
2.2、配置Redis配置信息
- spring
- redis:
- port: 6379
- host: 127.0.0.1
- password:
- database: 0
2.3、配置RedisConfig属性、如果需要使用FastJSON来序列化你的对象可以看看我前面写的一篇文章
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate initRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws Exception {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setDefaultSerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
2.4、写一个RedisLock工具类
/**
* @Description //直接使用Redis进行分布式锁
* 这是简易版本 如果要使用Redis原生锁记得加过期时间,防止死锁 最好使用Redisson操作简单更加方便
* @Date $ $
* @Author huangwb
**/
@Component
public class RedisLockCommon {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* Redis加锁的操作
*
* @param key
* @param value
* @return
*/
public Boolean tryLock(String key, String value) {
if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = stringRedisTemplate.opsForValue().get(key);
if (StringUtils.isNotEmpty(currentValue) && Long.valueOf(currentValue) < System.currentTimeMillis()) {
//获取上一个锁的时间 如果高并发的情况可能会出现已经被修改的问题 所以多一次判断保证线程的安全
String oldValue = stringRedisTemplate.opsForValue().getAndSet(key, value);
if (StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}
}
return false;
}
/**
* Redis解锁的操作
*
* @param key
* @param value
*/
public void unlock(String key, String value) {
String currentValue = stringRedisTemplate.opsForValue().get(key);
try {
if (StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)) {
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
}
}
}
2.5、减库存操作
@Override
public boolean decrementProductStore(Long productId, Integer productQuantity) {
String key = "dec_store_lock_" + productId;
long time = System.currentTimeMillis();
try {
//如果加锁失败
if (!redisLock.tryLock(key, String.valueOf(time))) {
return false;
}
ProductInfo productInfo = productInfoMapper.selectByPrimaryKey(productId);
//如果库存为空
if (productInfo.getProductStock() == 0) {
return false;
}
//减库存操作
productInfo.setProductStock(productInfo.getProductStock() - 1);
productInfoMapper.updateByPrimaryKey(productInfo);
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
//解锁
redisLock.unlock(key, String.valueOf(time));
}
return true;
}
2.6、测试接口
@GetMapping("test")
public String createOrderTest() {
if (!productInfoService.decrementProductStore(1L, 1)) {
return "库存不足";
}
OrderMaster orderMaster = new OrderMaster();
//未支付
orderMaster.setOrderStatus(0);
//未支付
orderMaster.setPayStatus(0);
orderMaster.setBuyerName("张三");
orderMaster.setBuyerAddress("湖南长沙");
orderMaster.setBuyerPhone("18692794847");
orderMaster.setOrderAmount(BigDecimal.ZERO);
orderMaster.setCreateTime(DateUtils.getCurrentDate());
orderMaster.setOrderId(UUID.randomUUID().toString().replaceAll("-", ""));
orderMasterService.insert(orderMaster);
}