一、使用lua脚本扣减单个商品的库存
@SpringBootTest
class LuaTests {
@Autowired
StringRedisTemplate stringRedisTemplate;
@Test
void test3() {
for (int i = 1; i <= 5; i++) {
stringRedisTemplate.opsForValue().set("product."+i,String.valueOf(i));
}
}
@Test
void test2() {
StringBuilder sb = new StringBuilder();
sb.append(" local key = KEYS[1] ");
sb.append(" local qty = ARGV[1] ");
sb.append(" local redis_qty = redis.call('get',key) ");
sb.append(" if tonumber(redis_qty) >= tonumber(qty) then ");
sb.append(" redis.call('decrby',key,qty) ");
sb.append(" return -1 ");
sb.append(" else ");
sb.append(" return tonumber(redis_qty) ");
sb.append(" end ");
RedisScript<Long> luaScript = RedisScript.of(sb.toString(), Long.class);
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 1; i <= 5; i++) {
executorService.execute(()->{
int qty = RandomUtil.randomInt(1,6);
Long count = stringRedisTemplate.execute(luaScript, CollUtil.newArrayList("product.5"),String.valueOf(qty));
if (count == -1L) {
System.out.println(Thread.currentThread().getId() + " 扣减成功,扣减了-> "+ qty);
} else {
System.out.println(Thread.currentThread().getId() + "扣减失败,需求量是:"+qty+",剩余库存量:"+count);
}
});
}
ThreadUtil.safeSleep(3000);
}
}
二、使用lua脚本扣减多个商品的库存
@Test
void test4() {
StringBuilder sb = new StringBuilder();
sb.append(" local table = {} ");
sb.append(" local values = redis.call('mget', unpack(KEYS) )");
sb.append(" for i = 1, #KEYS do ");
sb.append(" if tonumber(ARGV[i]) > tonumber(values[i]) then ");
sb.append(" table[#table + 1] = KEYS[i] .. '=' .. values[i] ");
sb.append(" end ");
sb.append(" end ");
sb.append(" if #table > 0 then ");
sb.append(" return table ");
sb.append(" end ");
sb.append(" for i = 1 , #KEYS do ");
sb.append(" redis.call('decrby',KEYS[i],ARGV[i]) ");
sb.append(" end ");
sb.append(" return {} ");
RedisScript<List> luaScript = RedisScript.of(sb.toString(), List.class);
List<StockProduct> stockProducts = new ArrayList<>();
stockProducts.add(new StockProduct(5,1));
stockProducts.add(new StockProduct(4,2));
List<String> keys = stockProducts.stream().map(it -> "product." + it.getId()).collect(Collectors.toList());
Object[] qtys = stockProducts.stream().map(it -> it.getQty() + "").toArray();
List<String> list = stringRedisTemplate.execute(luaScript,
keys,
qtys);
if(list.isEmpty()){
System.out.println("库存冻结成功");
} else {
for (String key_qty : list) {
String[] split = key_qty.split("=");
System.out.println(split[0] + "库存不足,剩余库存量:" + split[1]);
}
}
ThreadUtil.safeSleep(3000);
}
三、通过分布式锁,扣减商品的库存
package com.by;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import com.by.moder.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import javax.annotation.Resource;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@SpringBootTest
class SetNXTests {
@Autowired
StringRedisTemplate stringRedisTemplate;
@Resource(name = "redisTemplate")
ValueOperations<String,String> valueOperations;
@Test
void Test() {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 1; i <=5; i++) {
executorService.execute(()->{
String ioId = "IO"+ RandomUtil.randomInt(1,1000);
while (true){
Boolean b = valueOperations.setIfAbsent("lock.product.1",ioId+":"+ DateUtil.now());
if(b){
System.out.println(Thread.currentThread().getId()+"获取到了分布式锁");
ThreadUtil.safeSleep(3000);
stringRedisTemplate.delete("lock.product.1");
System.out.println(Thread.currentThread().getId()+"释放了分布式锁");
break;
}else {
System.out.println(Thread.currentThread().getId()+"没有获取到分布式锁");
ThreadUtil.safeSleep(1000);
}
}
});
}
ThreadUtil.safeSleep(100000);
}
}