1、使用数据库唯一索引:
为需要防重复的字段添加唯一索引,再尝试插入,如果重复会报错。
2、使用线程本地变量
利用ThreadLocal存储是否调用过的标识变量。
private static final ThreadLocal<Bolean> CALLED =new ThreadLocal<>(); if(CALLED.get() != null) { //已调用 } else { CALLED.set(true); //调用逻辑 }
3、使用synchronized同步块
private static final Object LOCK = new Object(); synchronized(LOCK) { if(isCalled) { //已调用 } else { isCalled = true; //调用逻辑 } }
4、使用AQS同步器实现锁
ReentrantLock、ReentrantReadWriteLock等。
5、使用全局变量加volatile
保证可见性,但不保证原子性。
6、使用数据库sequence生成唯一ID
避免业务重复调用。
7、redis锁。
防止重复调用的原理如下:
1、Redis锁
利用Redis的SETNX命令可以实现原子锁,保证同一时刻只有一个客户端能获取锁,其他客户端获取锁失败。通过设置过期时间实现锁的自动释放。
数据库唯一索引
为需要保证唯一的字段添加唯一索引约束。如果重复插入会报错,从而保证不重复执行业务逻辑。
2、ThreadLocal
ThreadLocal利用线程局部变量存储是否调用过的标记。由于每个线程都有自己的副本,可以保证同一线程内不重复调用。
3、同步锁
synchronized通过监视器对象(锁)实现同步,保证同一时刻只有一个线程持有锁执行同步代码块。其他线程获取锁失败被阻塞。
4、全局变量
利用volatile修饰全局变量,保证变量在不同线程之间的可见性。但不保证原子性,可能还是存在重复调用的风险。
5、序列号
使用数据库序列生成全局唯一的ID,保证业务执行时使用的ID不重复,从而避免重复调用。
总之,这些方法通过加锁、唯一约束等手段,都可以保证同一时刻只有一个线程/客户端执行需要防重复的业务逻辑,从而达到防重复调用的目的。
使用Redis的setkey和getkey方法防止重复调用有一定的问题:
1、setkey和getkey操作不是原子性的,存在竞争条件。多个线程同时调用setkey设置值,那么getkey可能拿到旧值,导致重复调用。
2、getkey只能判断值是否存在,无法实现锁功能。无法保证同一时刻只有一个线程执行业务逻辑。
3、没有设置key的过期时间,key会永久存在Redis中,不利于资源回收。
所以使用setkey和getkey防止重复调用存在以下潜在问题:
1、可能导致重复调用,无法保证原子性。
2、无法实现锁功能,无法保证同时只有一个线程执行。
3、key资源无法自动回收,可能会长期占用Redis存储空间。
相比之下,使用Redis的SETNX命令实现锁功能会更可靠一些:
1、SETNX是原子操作,可以保证同时只有一个客户端获取锁成功。
2、可以设置key的过期时间,锁资源可以自动释放。
3、锁可以实现同步控制,保证同时只有一个线程执行业务逻辑。
所以,如果要使用Redis防止重复调用,推荐使用SETNX实现分布式锁,而不是直接使用setkey和getkey。这可以更好地保证原子性和同步控制。
业务层代码:
//分布式锁key String lockKey = ""; //防重复提交(通过Redis的setIfAbsent实现了分布式锁) if (!redisUtil.tryGetDistributeLock(lockKey, 2)) { //释放锁 redisUtil.releaseDistributeLock(lockKey); // 锁获取失败,直接返回 return new BaseModel(HttpUtil.failCode, "您的操作过于频繁,请稍后再试!", null); } Try{ }catch(){ }finally{ //释放锁 redisUtil.releaseDistributeLock(lockKey); }
redisutil:
//获取锁 public Boolean tryGetDistributeLock(String lockKey, int timeout) { return redisTemplate.opsForValue().setIfAbsent(lockKey, "1", timeout, TimeUnit.SECONDS); } //释放锁 public void releaseDistributeLock(String lockKey) { redisTemplate.delete(lockKey); }
标签:调用,重复,Redis,接口,保证,线程,lockKey From: https://www.cnblogs.com/wmy666/p/17867535.html