自己写了个简单的redis分布式锁

自己写了个简单的redis分布式锁

自己写了个简单的redis分布式锁

【注意:此锁需要在每次使用前都创建对象,也就是要在线程内每次都创建对象后使用】

package redis;

import java.util.Collections;
import java.util.Random;
import java.util.UUID; import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config; import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool; public class RedisLock2 { JedisPool jedisPool;
private String key;
private String value;
//默认锁超时时间5秒
private Long timeout = 5L;
//加锁成功的时间起点
private Long startTime; /**
* 使用默认超时时间
* @param JedisPool
* @param key
*/
public RedisLock2(JedisPool JedisPool,String key) {
super();
this.jedisPool = JedisPool;
this.key = key;
value = System.currentTimeMillis()+"";
} /**
* 单独设置超时时间
* @param JedisPool
* @param key 锁的名称
* @param timeout 锁超时时间
*/
public RedisLock2(JedisPool JedisPool,String key,Long timeout) {
super();
this.jedisPool = JedisPool;
this.key = key;
this.timeout = timeout;
value = UUID.randomUUID().toString();
// System.out.println("创建锁时的value:"+value);
} /**
* 单次加锁,需要判断返回值【适用于获取不到锁就返回的业务场景】
* @return true:加锁成功; false:加锁失败
*/
public boolean lock() {
Jedis jedis = jedisPool.getResource();
boolean ok = jedis.set(key,value, "nx", "ex", timeout)!=null;
jedis.close();
if (!ok) {
//加锁失败
return false;
}
//加锁成功
startTime = System.currentTimeMillis();
return true;
} /**
* 阻塞重试加锁,默认时间间隔10毫秒
*/
public void lockRetry() {
lockRetry(null);
} /**
* 阻塞重试加锁,直到加锁成功【适用于一定要执行的业务】
* @param interval 重试时间间隔毫秒值
*/
public void lockRetry(Long interval) {
if (interval==null) {
//默认10毫秒重试一次
interval = 10L;
}else if(interval==0) {
//如果传值为0,则取10以内随机值
interval = (long) (new Random().nextInt(10) + 1);
}
int num = 0;
while (true) {
boolean b = lock();
num++;
if (b) {
System.out.println("加锁 "+num+" 次后成功");
break;
}
try {
//休息10毫秒后重试
Thread.sleep(interval);
System.out.println("有并发,休息:"+interval+"毫秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} /**
* 解锁【首先尝试用lua方式,如果redis的版本不支持lua,用普通方式】
* @return
*/
public String unlock() {
//解锁返回消息(非正常解锁时会打印,还可以在业务中解锁时判断此返回值)
String msg = "";
//加锁到解锁消耗时间
long timeConsume = System.currentTimeMillis() - startTime;
//超时直接返回
if (timeConsume > timeout * 1000) {
System.out.println("出现超时解锁-----------key:" + key + ",耗时毫秒:" + timeConsume);
// return false;
msg = "出现超时解锁--key:" + key + ",耗时毫秒:" + timeConsume;
return msg;
}
//这里是为了避免超时后,释放掉其他线程的同名锁
String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";
Jedis jedis = jedisPool.getResource();
//执行lua脚本返回值
Object evalRtn = 0;
try {
evalRtn = jedis.eval(luaScript, Collections.singletonList(key), Collections.singletonList(value));
// System.out.println("lua解锁返回值:"+evalRtn);
} catch (Exception e) {
e.printStackTrace();
//如果当前redis不支持lua脚本,用下面方法,但是下面的代码不是原子操作,可能会有并发问题,这里忽略
String s = jedis.get(key);
if (value.equals(s)) {
//释放锁
jedis.del(key);
// return true;
msg = "解锁成功";
return msg;
}
}finally {
jedis.close();
}
if ((Long) evalRtn == 1) {
// return true;
msg = "解锁成功";
return msg;
}
System.out.println("出现其他异常解锁失败---------key:" + key);
// return false;
msg = "出现其他异常解锁失败--key:" + key;
return msg;
} //简单加锁测试
public static void main1(String[] args) throws InterruptedException {
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
//设置超时时间100秒
RedisLock2 redisLock = new RedisLock2(jedisPool,"lock1",100L);
//加锁
boolean lock = redisLock.lock();
//获取到锁才执行,获取不到不执行
if(lock) {
//执行业务逻辑(要保证业务逻辑执行时间小于锁超时时间)
System.out.println("我获取到锁了");
// Thread.sleep(2000);
}
System.out.println(redisLock.unlock());
} //开启多线程往redis中设置值,保证不覆盖(用单次锁在业务逻辑中循环阻塞)
public static void main2(String[] args) {
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
//原来是使用网上的一种锁
// DistributedLock lock = new DistributedLock(jedisPool); for (int i = 0; i < 3; i++) {
// final int k = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
int k = j;
// 连接本地的 Redis 服务
// Jedis jedis = new Jedis("localhost");
// String code = null;
RedisLock2 myLock = null;
try {
//加分布式锁
// code = lock.lock("mylock");
myLock = new RedisLock2(jedisPool,"lock1",10L);
while (true) {
//不断获取锁
boolean lock = myLock.lock();
if (lock) {
//如果获取到则执行
// 从连接池中获取一个jedis对象
Jedis jedis = jedisPool.getResource();
if (!jedis.exists("a" + k)) {
jedis.set("a" + k, Thread.currentThread().getName());
jedis.expire("a" + k, 60);
System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName()
+ "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName());
try {
// Thread.sleep((long) (Math.random()*1000));
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: "
+ jedis.get("a" + k));
}
jedis.close();
break;//跳出循环
} } } finally {
//释放分布式锁
// lock.unLock("mylock",code);
//执行完解锁
myLock.unlock();
}
}
}
}).start();
}
} //开启多线程往redis中设置值,保证不覆盖(用阻塞锁)
public static void main(String[] args) {
//原来是使用rdissen锁
//获取redisson
// Config config = new Config();
// config.useSingleServer().setAddress("redis://localhost:6379");
// RedissonClient redisson = Redisson.create(config);
// //获取锁
// RLock lock = redisson.getLock("mylock"); // 创建连接池对象
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); for (int i = 0; i < 3; i++) {
// final int k = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
int k = j;
// 连接本地的 Redis 服务
// Jedis jedis = new Jedis("localhost");
RedisLock2 myLock = null;
try {
//加redisson分布式锁
// lock.lock();
//用我们自己写的重试锁【自定义锁非可重入锁,需要在线程中每次使用时都创建一个锁对象,多线程中只要名称相同就认为是同一个锁】
myLock = new RedisLock2(jedisPool,"lock1",10L);
//开启阻塞锁
myLock.lockRetry(3L);
//执行业务逻辑【因为用阻塞锁,无需判断返回值】
// 从连接池中获取一个jedis对象
Jedis jedis = jedisPool.getResource();
if (!jedis.exists("a" + k)) {
jedis.set("a" + k, Thread.currentThread().getName());
jedis.expire("a" + k, 60);
// System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName()
// + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName());
try {
// Thread.sleep((long) (Math.random()*1000));
} catch (Exception e) {
e.printStackTrace();
}
} else {
// System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: "
// + jedis.get("a" + k));
}
jedis.close();
} finally {
//释放redisson分布式锁
// lock.unlock();
//用我自己定义的锁
myLock.unlock();
}
}
}
}).start();
} } }
05-11 10:57