import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;
import java.util.List;
import java.util.UUID;
public class Test {
private static JedisPool jedisPool;
static{
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(20);
jedisPoolConfig.setMaxIdle(10);
jedisPool = new JedisPool(jedisPoolConfig,"39.106.68.198",6379,10000,"123");
}
public static Jedis getJedis() throws Exception {
if(null != jedisPool){
return jedisPool.getResource();
}
throw new Exception("Jedispool was not init");
}
/**
* 获取分布式锁
* @param key
* @param timeout 超时时限
* @return
*/
public String getLock(String key,int timeout){
//TODO 过期时间从缓存中取 配置化
try {
//获得客户端实例
Jedis jedis = Test.getJedis();
//生成随机锁号,解锁时用于判断,是否为当前线程持有锁
String value = UUID.randomUUID().toString();
//不能无限等待,超出时间放弃自旋
long end = System.currentTimeMillis() + timeout;
//判断是否超出时限 并且阻塞
while(System.currentTimeMillis()<end){
//设置 setnx 成功后 返回1,否则为0
if(jedis.setnx(key,value)==1){
jedis.expire(key,timeout);
return value;
}
//如果锁获取成功 reids挂掉,判断锁永久有效,再次设置失效时间
if(jedis.ttl(key)==-1){
jedis.expire(key,timeout);
}
//不能无限占用资源 设置休眠,然后再次获取锁
Thread.sleep(1000);
}
} catch (Exception e) {
//TODO 日志
e.printStackTrace();
}
return null;
}
/**
* 释放锁(判断是否为当前线程持有)
* @param key
* @param value
* @return
*/
public boolean releaseLock(String key,String value){
try {
Jedis jedis = Test.getJedis();
while (true) {
//监控一个或多个KEY 一旦KEY 被删除 ,后面事务的代码不会被执行
jedis.watch(key);
//判断是否为当前线程持有锁
if(value.equals(jedis.get(key))) {
//开启事务
Transaction transaction = jedis.multi();
transaction.del(key);
List<Object> list = transaction.exec();
//没有指令集执行成功 解锁失败
//TODO 关注事务执行过程(监视解锁前 人为变动锁可能会出现的问题)
if (list == null) {
continue;
}
System.out.println("锁释放成功,key【"+key+"】,value【"+value+"】");
return true;
}
jedis.unwatch();
break;
}
} catch (Exception e) {
//TODO 添加日志
System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】解锁失败"+e.getCause());
e.printStackTrace();
}
return false;
}
public static void main(String[] args) {
Test test = new Test();
String key = "lock:testLock1";
String lockId = test.getLock(key,10000);
if(null != lockId){
System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】获得锁成功,"+"锁KEY【"+key+"】"+"锁序号:【"+lockId+"】");
}else{
System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】获得锁失败");
}
test.releaseLock("lock:testLock1",lockId);
System.out.println("完成");
}
}
import com.example.demo.conf.JedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
public class RedisController {
@Autowired
private JedisUtil jedisUtil;
@RequestMapping("wordsList")
public void redis(String userId) {
String lockKey = userId;//锁key
String requestId = UUID.randomUUID().toString();//请求标识
final int EXPIRED_TIME = 300 * 1000;//redis 数据存储过期时间
//加锁举例
boolean lockResult = jedisUtil.tryGetDistributedLock(lockKey, requestId, EXPIRED_TIME);
System.out.println("加锁成功,"+lockResult);
//放锁举例
boolean releaseResult = jedisUtil.releaseDistributedLock(lockKey, requestId);
System.out.println("放锁成功,"+releaseResult);
}
}
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=39.106.68.198
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=123
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
package com.example.demo.conf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
protected static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxTotal;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.timeout}")
private int timeout;
@Bean
public JedisPool jedisPool() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxTotal);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
logger.info("JedisPool注入成功!!");
logger.info("redis地址:" + host + ":" + port);
return jedisPool;
}
}
package com.example.demo.conf;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Collections;
/**
* 不采用springTemplate 的操作类
* 因为springTemplate 的SetNx 非原子性,可能导致锁永久锁住,释放失败
*/
@Component("jedisUtil")
public class JedisUtil {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final Long RELEASE_SUCCESS = 1L;
@Autowired
private JedisPool jedisPool;
/**
* 尝试获取分布式锁
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
Jedis jedis = null;
try{
jedis = jedisPool.getResource();
// Long result = jedis.setnx(lockKey,requestId);
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
} finally {
//归还 jedis 连接
if(jedis != null){
jedis.close();
}
}
return false;
}
/**
* 释放分布式锁
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public boolean releaseDistributedLock(String lockKey, String requestId) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
} finally {
//归还 jedis 连接
if(jedis != null){
jedis.close();
}
}
return false;
}
/**
* 设置值并设置超时时间
* @param key
* @param value
* @param expireTime
* @return
*/
public Long rpushString(String key, String value, int expireTime){
Jedis jedis = null;
Long result = 0L;
try {
jedis = jedisPool.getResource();
result = jedis.rpush(key, value);
if(result > 0){
jedis.expire(key,expireTime);
}
} finally {
if(jedis != null){
jedis.close();
}
}
return result;
}
/**
* 批量插入
* @param key
* @param values
* @param expireTime
* @return
*/
public Long batchRpushString(String key, String[] values, int expireTime){
Jedis jedis = null;
Long result = 0L;
try {
jedis = jedisPool.getResource();
result = jedis.rpush(key, values);
if(result > 0){
jedis.expire(key,expireTime);
}
} finally {
if(jedis != null){
jedis.close();
}
}
return result;
}
/**
* 释放锁
* @param lockKey
*/
public Long releaseLock(String lockKey){
Long result = 0L;
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
result = jedis.del(lockKey);
} finally {
if(jedis != null){
jedis.close();
}
}
return result;
}
}
无论是单类版的jedis.setnx 还是整合版的jedis.set();都是通过Jedis类中的 SafeEncoder.encode(key)进行加锁的原理
链接: https://pan.baidu.com/s/1M7OhtwFy6NflAiMkLClKdQ
提取码:fh1h