1.初始化
public class RedisLock {
static Jedis jedis = new Jedis("127.0.0.1", 6379);
static String kill_key = "goods-1-1-1-num";
static String lock_key = "lock-goods-1-1-1";
public static void init() {
jedis.set(kill_key, "500");
String num = jedis.get(kill_key);
System.out.println(num);
}
}
2.setnx 和expire
- 通过 jedis.setnx(lock_key, "lock") 的返回值来判断是否获取锁(即能不能写入值),返回1即获取锁,返回0则失败
- 为了避免宕机而没有释放锁,给锁设置存活时间
- 下面代码中 System.exit(10); 模拟了宕机,finally 里的释放锁代码没有执行,可通过注释并执行两次main 函数来验证
public class RedisLock {
static Jedis jedis = new Jedis("127.0.0.1", 6379);
static String kill_key = "goods-1-1-1-num";
static String lock_key = "lock-goods-1-1-1";
public static void main(String[] args) throws Exception {
jedis.del(lock_key);
demo1();
//第二次注释掉上面两句代码,打印下面代码得到0,说明不能获取锁
//System.out.println(jedis.setnx(lock_key, "lock"));
}
public static void demo1() {
if (jedis.setnx(lock_key, "lock") == 1) {
System.exit(10); // 退出虚拟机模拟宕机。。
jedis.expire(lock_key, 5);
try {
// do something
} finally {
jedis.del(lock_key);
}
}
}
}
3.通过 jedis.set(lock_key, "lock", "nx", "ex", 5) 保证原子性来解决2 中的问题
- 第三个参数:只能填 nx(不存在则赋值) 或 xx(存在则赋值)
- 第四个参数:ex(秒)或 px(毫秒)
if ("ok".equals(jedis.set(lock_key, "lock", "nx", "ex", 5))) {
try {
// do something
} finally {
jedis.del(lock_key);
}
}
4.时间问题
- 规定时间内线程 A 的任务还没完成,而锁过期则自动释放了
- 此时线程 B 则获取了锁,此时两个线程同时执行任务
- 线程 A 完成任务,执行finally 把 B 的锁给释放了
用Lua 脚本。。。暂时不会。。。
5.使用守护线程
- 守护线程中设置一定时间后执行判断 key 的剩余的存活时间,若能执行判断说明当前的执行业务的线程还没结束,而且 key 的存活时间剩余不多了,可以在此时为key “续命”,当然可以隔一段时间判断。
- 下面例子设置 key 的生存时间为15秒,若过了10秒剩余5秒还没完成的时候,守护线程会为 key 续命为10秒。
public class RedisLock {
Jedis jedis = new Jedis("127.0.0.1", 6379);
String kill_key = "goods-1-1-1-num";
String lock_key = "lock-goods-1-1-1";
@Test
public void main() {
doSomething();
}
@Test
public void test() {
Long ttl = jedis.ttl(lock_key);
System.out.println(ttl);
}
public void doSomething() {
String isOk = jedis.set(lock_key, "lock", "nx", "ex", 15);
System.out.println(isOk);
// 给key设置了15秒的超时时间,而线程20秒才执行完
if ("OK".equals(isOk)) {
try {
createDaemon().start();
// do something over time
Thread.sleep(20000); // 假设超过15秒还没执行完
} catch (Exception e) {
e.printStackTrace();
}
finally {
jedis.del(lock_key);
}
}
}
public Thread createDaemon() {
Thread daemon = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("开始执行");
// 延迟10秒,若还能有判断,则程序还没执行完,finally 还没运行
Thread.sleep(10000);
System.out.println("睡完");
Long ttl = jedis.ttl(lock_key);
System.out.println(ttl);
if ( ttl <= 5 && ttl > 0) {
jedis.expire(lock_key, 10);
System.out.println("续命成功");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
daemon.setDaemon(true);
return daemon;
}
}