package com.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class RedisDistributedLock implements Lock {
@Autowired
private RedisTemplate redisTemplate;
private String lockName; //KEYS[1]
private String uuidValue; //ARGV[1]
private Long expireTime; //ARGV[2]
public RedisDistributedLock(RedisTemplate redisTemplate, String lockName) {
this.redisTemplate= redisTemplate;
this.lockName = lockName;
this.uuidValue=java.util.UUID.randomUUID().toString().replaceAll("-","")+":"+Thread.currentThread().getId();
this.expireTime=50L;
}
@Override
public void lock() {
//对外暴露的是lock与unlock,但实际调用得是tryLock,然后在调用tryLock(-1L,TimeUnit.SECONDS)
tryLock();
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
try { tryLock(-1L,TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); }
return false;
}
//实际干活得方法
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
if(time==-1){
String script=
"if redis.call('exists',KEYS[1]) == 0 " +
"or redis.call('hexists',KEYS[1],ARGV[1]) == 1 then" +
"redis.call('hincrby',KEYS[1],ARGV[1],1)" +
"redis.call('expire',KEYS[1],ARGV[2])" +
"return 1 " +
"else " +
"return 0 " +
"end";
Object execute = redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime));
Boolean flag=(Boolean) execute;
while(!flag){
}
}
return false;
}
//下面这两个方法用不到
@Override
public void unlock() {
String script=
"if " +
"redis.call('hexists',KEYS[1],ARGV[1]) == 0 then " +
"return nil" +
"elseif redis.call('hincrby',KEYS[1],ARGV[1],-1) == 0 then" +
"return redis.call('del',KEYS[1])" +
"else return 0" +
"end";
Object execute = redisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList(lockName), uuidValue);
Long flag=(Long) execute;
if(flag==null){
throw new RuntimeException("锁不存在");
}
}
//自动续期
private void renewExpire(){
String script="" +
"if redis.call('hexists',KEYS[1],ARGV[1]) ==1 then" +
"return redis.call('expire',KEYS[1],ARGV[2])" +
"else return 0 " +
"end";
//每隔一定时间就去执行一次
new Timer().schedule(new TimerTask() {
@Override
public void run() {
Boolean execute =(Boolean) redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime));
if(execute){
renewExpire();
}
}
},(this.expireTime*1000)/2);
}
@Override
public Condition newCondition() {
return null;
}
}
Redis实现分布式锁
于 2023-09-11 17:24:25 首次发布