- 实现原理是什么?
获取锁时,判断该key是否存在,存在的话证明已被使用,则阻塞。所以采用setnx命令来操作,即不存在则插入,返回0证明已存在,返回1证明不存在且写入成功,我们规定,返回1则获取锁成功,进行相应的操作和返回。 - 为什么要设置超时时间?
若是不设置超时时间,在unlock执行之前锁的系统挂掉,则此锁一直处于占用状态 - 若是业务处理时间大于设置的超时时间怎么办?
所以需要使用子线程来更新超时时间
代码理解
import redis.clients.jedis.Jedis;
public class RedisLock {
private final Jedis jedis;
private final int timeExpired;
private boolean shouldRenew = true;
private long lastTime;
public RedisLock(String ip) {
//有密码的自己单独修改
jedis = new Jedis(ip.split(":")[0], 6379);
timeExpired = 20;
}
public RedisLock(String ip, int timeExpired) {
jedis = new Jedis(ip.split(":")[0], 6379);
this.timeExpired = timeExpired;
}
public void getLock() {
while (true) {
Long setState = jedis.setnx("locks", "");
if (setState == 1L) {
//设置过期时间
jedis.expire("locks", timeExpired);
lastTime = System.currentTimeMillis();
new Thread(new Runnable() {
@Override
public void run() {
while (shouldRenew) {
final long nowTime = System.currentTimeMillis();
if (nowTime - lastTime > timeExpired / 3 * 1000) {
//子线程重置超时时间, 防止超时时间过去了,锁的业务还没处理完毕
lastTime = nowTime;
jedis.expire("locks", timeExpired);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
return;
}
}
}
public void unLock() {
//思考: 先置为false 还是 先del? 二者有区别吗
shouldRenew = false;
jedis.del("locks");
}
}
测试代码
public class LockTest {
public static void main(String[] args) throws InterruptedException {
final RedisLock locks1 = new RedisLock("127.0.0.1");
final RedisLock locks2 = new RedisLock("127.0.0.1");
System.out.println("连接完毕");
new Thread(() -> {
try {
locks1.getLock();
System.out.println("线程1获取到锁");
Thread.sleep(30000);
System.out.println("线程1释放锁");
locks1.unLock();
} catch (Exception e) {
}
}).start();
new Thread(() -> {
try {
locks2.getLock();
System.out.println("线程2获取到锁");
Thread.sleep(30000);
System.out.println("线程2释放锁");
locks2.unLock();
} catch (Exception e) {
}
}).start();
Thread.sleep(1000000000);
}
}