用法一:
//redis分布式部署一致性锁同步执行方法
RedisLock redisLock = new RedisLock("followSomeone_" + userId + targetUserId);
try {
//加锁(成功返回true,失败或超时返回false)
if(!redisLock.lock()){
logger.error("redis锁阻塞,获取超时。" + redisLock.getKey());
throw new Exception("系统繁忙,操作失败");
}
ghomeUserFollowBo.followSomeone(param.getUserId(), param.getTargetUserId(), param.getFollow());
} finally {
//任何情况下都要释放锁
redisLock.unlock();
}
用法二:
//redis分布式部署一致性锁同步执行方法
new RedisLock.SyncRun("activityEnroll_"+ activityId){
@Override
protected void run() throws Exception {
ghomeActivityEnrollBo.activityEnroll(param);
}
}.start();
Redis实现分布式部署一致性锁,相关知识可自行查询,直接上代码,有问题欢迎指正:
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.redis.core.RedisTemplate;
/**
* @ClassName: RedisLock
* @Description: 分布式部署一致性锁
*/
@Component
public class RedisLock {
private static JedisCluster jedisCluster;
@Autowired
public void setJedisCluster(JedisCluster jedisCluster){
RedisLock.jedisCluster = jedisCluster;
}
// 锁标志对应的key
private String key;
// 锁的后缀
private static final String LOCK_SUFFIX = "LOCK_";
// 存储到redis中的锁标志
private static final String LOCKED = "LOCKED";
// 请求锁重试时间(ms)
private int acquire_retry_ms = 100;
// 线程请求锁的等待时间(ms)
private int timeout_ms = 5 * 1000;
// 锁的有效时间(s),防止线程在入锁以后阻塞后面的线程无法获取锁
public int expireTime_s = 60;
// 是否锁定标志
private volatile boolean isLocked = false;
/**
* 构造器
* @param key 锁的key(类似于锁的ID)
*/
public RedisLock() {}
public RedisLock(String key) {
this.key = LOCK_SUFFIX + key;
}
public RedisLock(int key) {
this.key = LOCK_SUFFIX + key;
}
public RedisLock(String key, int timeout_ms) {
this(key);
this.timeout_ms = timeout_ms;
}
public RedisLock(String key, int timeout_ms, int expireTime_s) {
this(key, timeout_ms);
this.expireTime_s = expireTime_s;
}
public String getKey() {
return this.key;
}
/**
* 加锁(成功返回true,失败或超时返回false)
*/
public boolean lock(){
try {
//请求锁超时时间(到期时间)
long timeout = System.nanoTime() + timeout_ms * 1000000;
while (!isLocked) {
//将锁作为key存储到redis缓存中,存储成功则获得锁
if(jedisCluster.setnx(key, LOCKED) > 0){
//设置锁的有效期,也是锁的自动释放时间,也是一个客户端在其他客户端能抢占锁之前可以执行任务的时间
//可以防止因异常情况无法释放锁而造成死锁情况的发生
jedisCluster.expire(key, expireTime_s);
isLocked = true;
//上锁成功结束请求
break;
}
//睡眠N毫秒(加随机X纳秒)后继续请求锁
//获取锁失败时,应该在随机延时后进行重试,避免不同客户端同时重试导致谁都无法拿到锁的情况出现
Thread.sleep(acquire_retry_ms, new Random().nextInt(500));
//不断循环向Master节点请求锁,当请求时间(System.nanoTime() - nano)超过设定的超时时间则放弃请求锁,防止阻塞过长时间
if(System.nanoTime() > timeout){
break;
}
}
} catch (Exception e) {
e.printStackTrace();
// throw new Exception("redis锁阻塞,获取超时。" + getKey(), e);
}
return isLocked;
}
/**
* 释放锁(任何情况下加锁后都要释放锁,放在finally中)
*/
public void unlock() {
//不管请求锁是否成功,只要已经上锁,客户端都会进行释放锁的操作
jedisCluster.del(key);
isLocked = false;
}
/**
* redis分布式部署同步执行类
* @author tangf
*/
public static abstract class SyncRun {
private RedisLock redisLock;
public SyncRun(String key){
redisLock = new RedisLock(key);
}
protected Log logger = LogFactory.getLog(getClass());
/**
* 启动redis分布式部署同步执行方法
*/
public void start() throws Exception{
//加锁(成功返回true,失败或超时返回false)
if(!redisLock.lock()){
logger.error("redis锁阻塞,获取超时。" + redisLock.getKey());
throw new Exception("系统繁忙,操作失败");
}
logger.info(System.currentTimeMillis() + " redis锁:" + redisLock.getKey());
try {
//执行
this.run();
} finally {
//任何情况下都要释放锁
redisLock.unlock();
logger.info(System.currentTimeMillis() + " redis释放锁:" + redisLock.getKey());
}
}
/**
* 待重写的redis分布式部署同步执行方法
*/
protected abstract void run() throws Exception;
}
}