锁: 保证程序的原子性操作。
我们在单台服务器运行多线程的时候,会出现重买超卖的情况,这个时候可以通过加锁来保证代码的原子性操作,但是当我们多台服务器对应一个数据库的时候,依旧会出现线程安全问题,这个时候该怎么解决
redis解决分布式锁
实现代码
@Service
public class ProductStockServiceImpl2 implements ProductStockService {
@Autowired
private ProductStockDao productStockDao;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public String decreaseStock(Integer productId) {
final ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
Boolean cjj = forValue.setIfAbsent("product:" + productId, "cjj", 30, TimeUnit.SECONDS);//todo 获得锁,如果为true代表获取锁成功
if(cjj){//todo 如果获取的返回值为true,则表示获取分布锁成功
try{
//查看该商品的库存数量
Integer stock = productStockDao.findStockByProductId(productId);
if (stock > 0) {
//修改库存每次-1
productStockDao.updateStockByProductId(productId);
System.out.println("扣减成功!剩余库存数:" + (stock - 1));
return "success";
} else {
System.out.println("扣减失败!库存不足!");
return "fail";
}
}finally {
stringRedisTemplate.delete("product:"+productId);//todo 删除key表示释放锁资源
}
}else{
try {
Thread.sleep(200);//休息200毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
return decreaseStock(productId);
}
}
测试结果
缺陷: 当程序执行时间超过锁的时间。还是会发生线程安全问题
解决: 使用watch dog机制。----第三方框架完成redisson
使用watch dog机制 解决当程序执行时间超过锁的时间导致的线程安全问题
(1)引入redissson依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
(2)自己创建redisson对象并交予springioc容器管理
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer()
// use "rediss://" for SSL connection
.setAddress("redis://192.168.94.131:6379");
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
(3)在service中使用
@Autowired
private ProductStockDao productStockDao;
@Autowired
private RedissonClient redissonClient;//redisson锁对象,用来解决程序执行超时问题和分布式线程安全问题
@Override
public String decreaseStock(Integer productId) {
RLock lock = redissonClient.getLock("product:"+productId);
try{
lock.lock(30,TimeUnit.SECONDS); //这里的锁时间必须是30s
//查看该商品的库存数量
Integer stock = productStockDao.findStockByProductId(productId);
if (stock > 0) {
//修改库存每次-1
productStockDao.updateStockByProductId(productId);
System.out.println("扣减成功!剩余库存数:" + (stock - 1));
return "success";
} else {
System.out.println("扣减失败!库存不足!");
return "fail";
}
}finally {
lock.unlock();//释放锁
}
}