redis实现:
缺点: 1, 获取锁的结点服务器的线程如果意外中止(没有释放锁),则锁要等超时才能释放, 2, 应用要在过期时间内处理完业务,锁的时间过期后,会自动释放.
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* redis相关的工具
* @author Justin
*/
public class RedisTool {
private static String LOCK_PREFIX = "basisLock:";
/**
* 锁的默认时长
*/
public static Long LOCK_DEF_MILLI = 60 * 1000L;
/**
* 全局收货地址变更时,收集通知时,为了控制进度添加的锁
*/
public static String LOCK_GSA = "gsa:";
/**
* redis的简易分布式锁
* 参考: https://www.cnblogs.com/kiko2014551511/p/11801138.html
* https://www.jianshu.com/p/6dd656e7051f
* @param rt
* @param key
* @param lockExpireMils
* @return
*/
public static boolean tryLock(RedisTemplate rt, String key, long lockExpireMils){
String lock = LOCK_PREFIX + key;
RedisSerializer keySerl = rt.getKeySerializer();
return (Boolean) rt.execute((RedisCallback) connection -> {
long nowTime = System.currentTimeMillis();
long expireAt = nowTime + lockExpireMils + 1;
Boolean acquire = connection.setNX(keySerl.serialize(lock), String.valueOf(expireAt).getBytes());
if (acquire) {
// 设置key的过期时间
rt.expire(lock, lockExpireMils-2, TimeUnit.MILLISECONDS);
return true;
} else {
byte[] value = connection.get(keySerl.serialize(lock));
if (Objects.nonNull(value) && value.length > 0) {
long expireTime = Long.parseLong(new String(value));
if (expireTime < nowTime) {// 过期了
byte[] oldValue = connection.getSet(keySerl.serialize(lock), String.valueOf(expireAt).getBytes());
if(oldValue==null){
// 锁被释放了
acquire = connection.setNX(keySerl.serialize(lock), String.valueOf(expireAt).getBytes());
if(acquire) {
rt.expire(lock, lockExpireMils - 2, TimeUnit.MILLISECONDS);
return true;
}
return false;
}
// 并发获取锁,如果得到的是过期的锁,则代表新锁设置成功
boolean lockOK = Long.parseLong(new String(oldValue)) < nowTime;
if(lockOK){
// 获取到过期的锁,才能续时
rt.expire(lock, lockExpireMils+1, TimeUnit.MILLISECONDS);
}
return lockOK;
}
}
}
return false;
});
}
/**
* redis的简易分布式锁-刷新锁
* 参考: https://www.cnblogs.com/kiko2014551511/p/11801138.html
* https://www.jianshu.com/p/6dd656e7051f
* @param rt
* @param key
* @param lockExpireMils
* @return
*/
public static void renewLock(RedisTemplate rt, String key, long lockExpireMils){
String lock = LOCK_PREFIX + key;
long expireAt = System.currentTimeMillis() + lockExpireMils + 1;
rt.opsForValue().set(lock, expireAt);
rt.expire(lock, lockExpireMils+1, TimeUnit.MILLISECONDS);
}
/**
* redis的简易分布式锁-释放锁
* 参考: https://www.cnblogs.com/kiko2014551511/p/11801138.html
* https://www.jianshu.com/p/6dd656e7051f
* @param rt
* @param key
* @return
*/
public static void unlock(RedisTemplate rt, String key){
String lock = LOCK_PREFIX + key;
rt.delete(key);
}
}
zookeeper实现:
缺点: 1, 没有锁超时时间,如果线程卡住了,锁一直不会释放(待确认), 2, 由于zookeeper的实现机制(在硬盘上写数据)的原因,性能不如redis
public static CuratorFramework clientCF; // zookeeper集群的代理对象。它是线程安全的
/**
* zookeeper Curator
* http://macrochen.iteye.com/blog/1366136/
* http://blog.youkuaiyun.com/yin380697242/article/details/52293771
https://segmentfault.com/a/1190000018813672
* @param args
*/
public static void initCurator(){
if(clientCF!=null){
return ;
}
try{
String zkUrl="192.168.1.177:2381,192.168.1.177:2382,192.168.1.177:2383";
// RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
//clientCF = CuratorFrameworkFactory.builder().connectString(zkUrl).namespace("/test3")
// .retryPolicy(retryPolicy).connectionTimeoutMs(5000).build();
clientCF = CuratorFrameworkFactory.builder().connectString(zkUrl).retryPolicy(new RetryNTimes(3, 1000))
.connectionTimeoutMs(5000).build();
clientCF.start();
}catch(Exception e){
log.info(e.getMessage(), e);
}
}
private static final String ZK_LOCK_PATH = "/zktest3";
private static void doWithLock(CuratorFramework client) {
InterProcessMutex lock = new InterProcessMutex(client, ZK_LOCK_PATH);
try {
if (lock.acquire(10 * 1000, TimeUnit.SECONDS)) {
Comn.pl(Thread.currentThread().getName() + " hold lock");
Thread.sleep(5000L);
Comn.pl(Thread.currentThread().getName() + " release lock");
}
} catch (Exception e) {
log.info(e.getMessage(), e);
} finally {
try {
lock.release();
} catch (Exception e) {
log.info(Thread.currentThread().getName()+" "+e.getMessage(), e);
}
}
}