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;
}
}