redis lock
單機lock
- synchronized
- reentrantLock
- cas
分散式lock
- redis lock
- zookeeper lock
- 資料庫行鎖
redis能稱為分散式鎖的條件
- 互斥性
- 原子性
redis加鎖流程
踩過的坑
執行緒A獲得redis的鎖,執行業務發生超時釋放鎖,執行緒B獲得執行緒A釋放的鎖,執行緒A業務執行結束,釋放了執行緒B的鎖 解決辦法:每個執行緒獲取鎖,設定不同value值,執行緒釋放鎖的時候,將原先執行緒設定的value與當前鎖的value做比較,相同時釋放鎖
優化的點
1、刪除鎖並非原子性操作
public void unlock() {
String value = convertValue();
//1.獲取lockValue
String lockValue = RedisProxyUtil.getCacheInstance().getJedisReturn().get(name);
//2.釋放鎖
if (value.equalsIgnoreCase(lockValue)) {
RedisProxyUtil.getCacheInstance().getJedisReturn().del(name);
}
}
複製程式碼
可能出現的情況 執行緒A同時出現超時和業務執行結束,那麼執行緒A會出現兩次釋放鎖,兩次釋放鎖第一步都走完了,其中一個釋放鎖的執行緒1、2都走完了,導致執行緒B獲得鎖,執行緒A另一個執行緒進行步驟二釋放了執行緒B的鎖
解決方法:可以使用lua指令碼進行原子刪除操作
public void unlock() {
String checkAndDelScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";
jedis.eval(checkAndDelScript, 1, lockKey, lockValue);
}
複製程式碼
2、自己實現redis鎖不易維護,可以使用Redisson,話不多說,上程式碼
/**
* @author jujunjun
* @version v1.0.0
* @date 2018/8/30
*/
public class RedissonTest {
public static RedissonClient redisson;
static {
Config config = new Config();
config.useSingleServer().setAddress("redis://10.30.130.180:6379");
redisson = Redisson.create(config);
}
public static void main(String[] args){
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i = 0; i < 5; i++) {
executorService.execute(()-> testLock());
}
}
private static void testLock() {
RLock rLock = redisson.getLock("junjunlock");
try {
/* 嘗試加鎖 */
boolean lock = rLock.tryLock(10, 10, TimeUnit.SECONDS);
if(lock) {
System.out.println(Thread.currentThread().getName()+ "------" + System.currentTimeMillis());
Thread.sleep(2000);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
/* 釋放鎖 */
rLock.unlock();
}
}
}
複製程式碼
zookeeper lock
流程
package com.code.web.service;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @author jujunjun
* @version v1.0.0
* @date 2018/8/31
*/
public class ZookeeperLock {
public static CuratorFramework client;
static {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.newClient("10.30.130.180:2181", retryPolicy);
}
public static void main(String[] args) throws Exception {
client.start();
InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock");
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i = 0; i < 5; i++) {
executorService.execute(()-> testLock(mutex));
}
}
private static void testLock(InterProcessMutex mutex) {
try {
mutex.acquire();
System.out.println(Thread.currentThread().getName()+ "------" + System.currentTimeMillis());
Thread.sleep(2000);
mutex.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製程式碼