package jedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
/**
* @author mawt
* @description
* @date 2020/8/7
*/
public class TestJedis {
private static Jedis jedis;
private static final String KEY = "store001";
static {
jedis = new Jedis("192.168.1.99");
jedis.set(KEY, "10");
}
public static void main(String[] args) {
}
private void deductStock() {
System.out.println("最简单的减库存代码:存在并发安全问题");
String left = jedis.get(KEY);
if (left != null && Integer.parseInt(left) > 0) {
int realStock = Integer.parseInt(left) - 1;
jedis.set(KEY, String.valueOf(realStock));
System.out.println("减库存成功,剩余库存为:" + realStock);
return;
}
System.out.println("减库存失败,库存不足");
}
private void deductStock2() {
System.out.println("减库存代码升级版2:使用重量级锁synchronized(jvm级别的锁),性能低下,针对不同物品可能需要不同的锁this,分布式环境下jvm锁失效");
synchronized (this) {
String left = jedis.get(KEY);
if (left != null && Integer.parseInt(left) > 0) {
int realStock = Integer.parseInt(left) - 1;
jedis.set(KEY, String.valueOf(realStock));
System.out.println("减库存成功,剩余库存为:" + realStock);
return;
}
System.out.println("减库存失败,库存不足");
}
}
private void deductStock3() {
System.out.println("减库存代码升级版3:使用setnx分布式锁,问题:上锁后程序崩溃时没来得及释放锁");
Long lock = jedis.setnx("lock001", "1");
if (lock == 1L) { //获取锁成功
String left = jedis.get(KEY);
if (left != null && Integer.parseInt(left) > 0) {
int realStock = Integer.parseInt(left) - 1;
jedis.set(KEY, String.valueOf(realStock));
System.out.println("减库存成功,剩余库存为:" + realStock);
return;
}
System.out.println("减库存失败,库存不足");
}
jedis.del("lock001");
}
private void deductStock4() {
System.out.println("减库存代码升级版4:使用setnx分布式锁(redis级别的锁),使用finally释放锁,问题:上锁后如果web挂掉来不及释放锁,其他线程永远拿不到锁了");
try {
Long lock = jedis.setnx("lock001", "1");
if (lock == 1L) { //获取锁成功
String left = jedis.get(KEY);
if (left != null && Integer.parseInt(left) > 0) {
int realStock = Integer.parseInt(left) - 1;
jedis.set(KEY, String.valueOf(realStock));
System.out.println("减库存成功,剩余库存为:" + realStock);
return;
}
System.out.println("减库存失败,库存不足");
}
} finally {
jedis.del("lock001");
}
}
private void deductStock5() {
System.out.println("减库存代码升级版5:设置锁的过期时间,问题:setnx和expire不是原子操作,如果setnx执行成功后程序挂掉了,4问题依然存在");
try {
Long lock = jedis.setnx("lock001", "1");
jedis.expire("lock001", 10); //如何在对应场景下确定锁的过期时间
if (lock == 1L) { //获取锁成功
String left = jedis.get(KEY);
if (left != null && Integer.parseInt(left) > 0) {
int realStock = Integer.parseInt(left) - 1;
jedis.set(KEY, String.valueOf(realStock));
System.out.println("减库存成功,剩余库存为:" + realStock);
return;
}
System.out.println("减库存失败,库存不足");
}
} finally {
jedis.del("lock001");
}
}
private void deductStock6() {
System.out.println("减库存代码升级版6:setnx原子命令:上锁同时给锁设置过期时间,问题:如下:");
try {
Long lock = jedis.setnx("lock001", "1");
//jedis没有方法:jedis.set(keys,args,"NX","PX",30000);
//可以使用SetParams代替:
// nx() if not exists的缩写 当key不存在时才会set
// ex(10) 设置过期时间10s
//相当于setnx命令
// SetParams p = new SetParams().nx().ex(10);
// String lock001 = jedis.set("lock001", "1", p);
String lock001 = jedis.set("lock001", "1", "NX", "EX", 10);
//存在的问题描述:
//1.线程A上锁成功并设置过期时间10秒后,准备执行减库存的代码,如果在这个时候比如有数据库并发高压力大,有可能
//会发生这样一种情况:减库存代码执行耗时超过10秒,准备释放锁时,因为分布式锁超时过期了,这个时候如果线程B来获取锁,锁过期被redis删除,
//线程B获取锁成功,尝试执行减库存的操作,但是线程A释放锁,这个锁并不是线程A上的锁,而是B释放的锁,
//这个时候线程B又会来重复执行线程A的逻辑,线程C又会来重复执行线程B的逻辑......造成锁被其他线程释放的问题
//大量高并发场景下可能会出现这样的问题,锁机制失效
if (lock == 1L) { //获取锁成功
String left = jedis.get(KEY);
if (left != null && Integer.parseInt(left) > 0) {
int realStock = Integer.parseInt(left) - 1;
jedis.set(KEY, String.valueOf(realStock));
System.out.println("减库存成功,剩余库存为:" + realStock);
return;
}
System.out.println("减库存失败,库存不足");
}
} finally {
jedis.del("lock001");
}
}
private void deductStock7() {
System.out.println("减库存代码升级版7:setnx时的value为uuid,只能删除自己上的锁");
String uuid = UUID.randomUUID().toString();
try {
Long lock = jedis.setnx("lock001", "1");
//jedis没有方法:jedis.set(keys,args,"NX","PX",30000);
//可以使用SetParams代替:
// nx() if not exists的缩写 当key不存在时才会set
// ex(10) 设置过期时间10s
//相当于setnx命令
// SetParams p = new SetParams().nx().ex(10);
// String lock001 = jedis.set("lock001", "1", p);
String lock001 = jedis.set("lock001", uuid, "NX", "EX", 10);
//存在的问题描述:
// 线程A在上锁成功后,如果在执行减库存的时候花费了大量的时间,在此时间内,锁存在失效的可能
// 如果锁失效了,线程B有可能会重新上锁,属于B的锁,此时锁失效,无法保持库存同步
if (lock == 1L) { //获取锁成功
String left = jedis.get(KEY);
if (left != null && Integer.parseInt(left) > 0) {
int realStock = Integer.parseInt(left) - 1;
jedis.set(KEY, String.valueOf(realStock));
System.out.println("减库存成功,剩余库存为:" + realStock);
return;
}
System.out.println("减库存失败,库存不足");
}
} finally {
String lock001 = jedis.get("lock001");
if (uuid.equals(lock001))
jedis.del("lock001");
}
}
private void deductStock8() {
System.out.println("减库存代码升级版8:setnx时的value为uuid,只能删除自己上的锁");
String uuid = UUID.randomUUID().toString();
try {
Long lock = jedis.setnx("lock001", "1");
//jedis没有方法:jedis.set(keys,args,"NX","PX",30000);
//可以使用SetParams代替:
// nx() if not exists的缩写 当key不存在时才会set
// ex(10) 设置过期时间10s
//相当于setnx命令
// SetParams p = new SetParams().nx().ex(10);
// String lock001 = jedis.set("lock001", "1", p);
String lock001 = jedis.set("lock001", uuid, "NX", "EX", 10);
if (lock == 1L) { //获取锁成功
new Thread(() -> {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//检查锁是否存在
String lock001 = jedis.get("lock001");
if (uuid.equals(lock001)) {
//锁还没有失效,给锁续命
jedis.expire("lock001", 10);
} else {
//当锁被删除后,停止调度器
cancel();
}
}
}, 1, 10 / 3 * 1000);
}).start();
String left = jedis.get(KEY);
if (left != null && Integer.parseInt(left) > 0) {
int realStock = Integer.parseInt(left) - 1;
jedis.set(KEY, String.valueOf(realStock));
System.out.println("减库存成功,剩余库存为:" + realStock);
return;
}
System.out.println("减库存失败,库存不足");
}
} finally {
String lock001 = jedis.get("lock001");
if (uuid.equals(lock001))
jedis.del("lock001");
}
}
}
一层一层实现高并发扣库存的问题
猜你喜欢
转载自blog.csdn.net/qq_33436466/article/details/107868078
今日推荐
周排行