import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* spring bean工具类,支持通过静态方法从容器中获取bean
*
* @author dengxiaoming
* @date 2019-12-26
*/
public class SpringBeanUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringBeanUtil.applicationContext = applicationContext;
}
public static T getBean(Class clazz) {
if (applicationContext == null) {
return null;
}
return applicationContext.getBean(clazz);
}
public static Object getBean(String name) throws BeansException {
if (applicationContext == null) {
return null;
}
return applicationContext.getBean(name);
}
public static ApplicationContext getCtx() throws BeansException {
return applicationContext;
}
}
import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
/**
* Redis工具类
*
* @author dengxiamoing
* @date 2020-02-19 15:40
*/
@SuppressWarnings("unchecked")
public class RedisUtil {
private static RedisTemplate redisTemplate;
/**
* 获取redisTemplate
*
* redisTemplate为空时从spring上下文中获取
*
* @return redisTemplate
*/
public static RedisTemplate getRedisTemplate() {
if (redisTemplate == null) {
synchronized (RedisUtil.class) {
if (redisTemplate == null) {
redisTemplate = (RedisTemplate) SpringBeanUtil.getBean("x7RedisTemplate");
}
}
}
return redisTemplate;
}
/**
* 对字符串类型的数据操作
*
* @return string类型操作对象
*/
public static ValueOperations valueOps() {
return getRedisTemplate().opsForValue();
}
/**
* 对hash类型的数据操作
*
* @return hash类型操作对象
*/
public static HashOperations hashOps() {
return getRedisTemplate().opsForHash();
}
/**
* 对列表类型的数据操作
*
* @return list类型操作对象
*/
public static ListOperations listOps() {
return getRedisTemplate().opsForList();
}
/**
* 对集合类型的数据操作
*
* @return set类型操作对象
*/
public static SetOperations setOps() {
return getRedisTemplate().opsForSet();
}
/**
* 对有序集合类型的数据操作
*
* @return zset类型操作对象
*/
public static ZSetOperations zSetOps() {
return getRedisTemplate().opsForZSet();
}
/**
* 删除指定键值数据
*
* @param key 键值
*/
public static void delete(String key) {
getRedisTemplate().delete(key);
}
/**
* 批量删除指定键值数据
*
* @param keys 键值列表
*/
public static void delete(List keys) {
getRedisTemplate().delete(keys);
}
/**
* 指定键值超时时间
*
* @param key 键值
* @param expireTime 超时时间
*/
public static void expire(String key, Duration expireTime) {
getRedisTemplate().expire(key, expireTime.toMillis(), TimeUnit.MILLISECONDS);
}
}
import io.netty.util.HashedWheelTimer;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
/**
* @author mojiazhu
* @date 2023/7/21 13:49
*/
public class RedisLock {
private final String redisKey;
public RedisLock(String redisKey) {
this.redisKey = redisKey;
}
/**
* 一直尝试加锁 可重入标识为 线程id
*/
public void lock() {
String reentryFlag = String.valueOf(Thread.currentThread().getId());
lock(reentryFlag);
}
/**
* 尝试一段时间枷锁 可重入标识为 线程id
*
* @param SpinTime 获取锁超时时间
*/
public void tryLock(int SpinTime) {
String reentryFlag = String.valueOf(Thread.currentThread().getId());
tryLock(reentryFlag, SpinTime);
}
/**
* 释放锁 可重入标识为 线程id
*/
public void unLock() {
String reentryFlag = String.valueOf(Thread.currentThread().getId());
unLock(reentryFlag);
}
/**
* 一直尝试加锁 可重入标识为 自定义标识
*/
public void lock(String reentryFlag) {
int expireTime = 30 * 1000;
while (true) {
boolean acquire = tryAcquire(reentryFlag, expireTime);
if (acquire) {
autoRenewal(reentryFlag);
return;
}
//后期请修改以下方法 休眠时间果断 线程多会长期占用cpu
try {
LockSupport.parkNanos(10);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
/**
* 尝试一段时间枷锁 可重入标识为 自定义标识
*
* @param SpinTime 获取锁超时时间
*/
public boolean tryLock(String reentryFlag, int SpinTime) {
int spinTime = 0;
int expireTime = 30 * 1000;
while (true) {
if (spinTime > SpinTime) {
return false;
}
boolean isGet = tryAcquire(reentryFlag, expireTime);
if (isGet) {
autoRenewal(reentryFlag);
return true;
}
try {
Thread.sleep(1000);
spinTime++;
} catch (Exception e) {
return false;
}
}
}
/**
* 释放锁 可重入标识为 自定义标识
*/
public void unLock(String reentryFlag) {
DefaultRedisScript
if(redis.call('exists', KEYS[1]) == 0) then -- 判断锁是否已存在
redis.call('hset', KEYS[1], ARGV[1], '1'); -- 不存在, 则获取锁
redis.call('expire', KEYS[1], ARGV[2]); -- 设置有效期
return true; -- 返回结果
end;
if(redis.call('hexists', KEYS[1], ARGV[1]) == 1) then -- 锁已经存在,判断重入标识是否是自己
redis.call('hincrby', KEYS[1], ARGV[1], '1'); -- 如果是自己,则重入次数+1
redis.call('expire', KEYS[1], ARGV[2]); -- 设置有效期
return true; -- 返回结果
end;
return false; -- 代码走到这里,说明获取锁的不是自己,获取锁失败
-------------------------------------------------------
if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then
-- 如果已经不是自己,则直接返回
return nil;
end;
-- 是自己的锁,则重入次数减一
local count = redis.call('hincrby', KEYS[1], ARGV[1], -1);
-- 判断重入次数是否已为0
if (count == 0) then
-- 等于 0,说明可以释放锁,直接删除
redis.call('del', KEYS[1]);
return nil;
end;
--------------------------------------------------------
if(redis.call('hexists', KEYS[1], ARGV[1]) == 1) then -- 判断锁是否已存在
redis.call('pexpire', KEYS[1], ARGV[2]); -- 设置有效期
return true; -- 返回结果
end;
return false;
高级lua脚本
if (redis.call('exists', KEYS[1]) == 0)
then
-- 加锁成功,并且设置过期时间
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- 如果锁存在
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)
then
-- 进行计数+1 (为了可重入)
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- 获取剩余过期时间
return redis.call('pttl', KEYS[1]);
----------------------------------------------------
if (redis.call('exists', KEYS[1]) == 0)
then
-- 通知抢锁。
redis.call('publish', KEYS[2], ARGV[1]);
--结束
return 1;
end;
-- 如果锁不存在,不处理
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0)
then
return nil;
end;
--对其中的元素进行计数-1 实现可重入
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
-- 如果此时还有计数
if (counter > 0)
then
-- 刷新过期时间
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0;
else
-- 解锁,通知其他线程争抢锁。
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
return nil;