package com.example.redislock.controller;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Controller
public class IndexController {
@Autowired
private Redisson redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("test_stock")
@ResponseBody
public String testStock() throws InterruptedException {
String lockKey = "product_001";
String contenId = UUID.randomUUID().toString();
RLock redissonLock = redisson.getLock(lockKey);
try {
// 先確認商品是否存在 1 保證只有一個線程能成功往下執行
// Boolean result = stringRedisTemplate.opsForValue().setIfAbsent("product_001", 100 + "");
// 有效期10s
//stringRedisTemplate.expire("product_001",10, TimeUnit.SECONDS);
// 把上面兩行合并成一行 要麽同時成功要麽同時失敗
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, contenId, 10, TimeUnit.SECONDS);
if (!result) {
return "error商品不存在了";
}
//stringRedisTemplate.opsForValue().set("stock", 100 + "");
System.out.println(stringRedisTemplate.opsForValue().get("stock") + "");
// 多个线程请求到这里 会一个线程拿到锁,其他线程阻塞在这里 while循环一直尝试加锁 // 也有非阻塞的api
redissonLock.lock(30, TimeUnit.SECONDS); // 底层实现就是往Redis里设置一个key value
// 如果加锁成功 会开启一个新线程 给锁续命
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减库存成功===剩余" + stock);
System.out.println("扣减库存成功===剩余" + realStock);
} else {
System.out.println("扣减库存失败===库存不足");
}
} finally {
redissonLock.unlock(); // 释放锁
if (contenId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
// 釋放鎖
stringRedisTemplate.delete(lockKey);
}
}
return "end";
}
// 全部實行需要 15s
// key 有效期 為10s
// 執行到10s時鎖已經被釋放了
// 第二個綫程 拿到鎖開始執行 黨第一個綫程繼續執行到15s時 八第二個綫程的鎖給釋放了
// 結果鎖永久失效
// 解決給每個綫程生成唯一標識 UUID
// 分佈式鎖的實現
// 拿到鎖 開啓分綫程 定時器每隔一段是按檢查自己加的這把鎖是否存在 每隔3/1 時間重新設置key 的生存時間 續命
// Redisson 底層的實現 也是開啓分綫程的方式
// 主從架構 主節點成功 沒有同步到從節點 沒同步的時候 主節點挂了 選舉新的主節點 這是key 還沒有同步過去呢
}
使用 Redisson 需要初始化Bean
@Configuration
public class RedissonClientConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Bean
public RedissonClient getRedisson(){
Config config = new Config();
config.useSingleServer().setAddress("redis://" + host + ":" + port);
return Redisson.create(config);
}
}