使用场景
同步锁,让业务方法在锁设定的时间内是同步执行的
-
redisService.setIfAbsent
-
redisService.expire
@PostMapping("/update")
public MMHResponse saveOrUpdateMemberBaby(@RequestBody SaveOrUpdateMemberBabyForm form){
GlobalLock globalLock = null;
try {
globalLock = GlobalLockRedisImpl.getInstance(key);
globalLock.lock();
//TODO ...业务方法
return SuccessResponse.newInstance();
} catch (Exception e) {
logger.error("[ebiz-user-service][saveOrUpdateMemberBaby]操作失败,原因{}", StackTraceLogUtil.getStackTraceAsString(e));
return ErrorResponse.newInstance("操作失败");
} finally {
//释放锁
if (globalLock != null) {
globalLock.unlock();
}
}
}
不难看出,redis不参与业务逻辑代码,仅仅作一个定时的开发,保证在某个时间段内 业务代码不受干扰
我们再来看一下lock 方法
GlobalLock
/** * <pre> * Example 1: 强制获取锁,会阻塞, 超时失败会抛出异常 * GlobalLock lock = GlobalLockRedisImpl.getInstance("local key name") * try{ * lock.lock(5); * doSamething() * } catch(GDSystemException e) { * //获取锁异常 * } finally { * lock.unlock(); * } * * * Example 2: 尝试获取锁, 失败会直接返回false * GlobalLock lock = ...; * if (lock.tryLock()) { * try { * // manipulate protected state * } finally { * lock.unlock(); * } * } else { * // perform alternative actions * } * </pre> */ public interface GlobalLock { /** * 申请加锁,此操作为阻塞操作,且不响应线程中断 * 超过指定时间会抛出异常,程序需要对异常处理, 最大锁住时间为1分钟 * * @throws RuntimeException 在指定的时间内获取锁失败 */ void lock(int timeout) throws RuntimeException; /** * 申请加锁, 最大时间30秒 * * @throws RuntimeException */ void lock() throws RuntimeException; /** * Acquires the lock only if it is free at the time of invocation. * <p> * <p>Acquires the lock if it is available and returns immediately * with the value {@code true}. * If the lock is not available then this method will return * immediately with the value {@code false}. * <p> * <p>A typical usage idiom for this method would be: * <pre> * Lock lock = ...; * if (lock.tryLock()) { * try { * // manipulate protected state * } finally { * lock.unlock(); * } * } else { * // perform alternative actions * } * </pre> * This usage ensures that the lock is unlocked if it was acquired, and * doesn't try to unlock if the lock was not acquired. * * @return {@code true} if the lock was acquired and * {@code false} otherwise */ boolean tryLock(); /** * 释放锁 */ void unlock(); }
GlobalLockRedisImpl
/** * 基于redis实现的全局锁 */ public class GlobalLockRedisImpl implements GlobalLock { private ILog logger = LogFactory.getLog(GlobalLockRedisImpl.class, LogBusinessModule.TRACE_LOG); private RedisService redisService; // timeout(ms) private static final int TIME_OUT = 30; // private Jedis jedis; private String key; // state flag private volatile boolean locked = false; private static ConcurrentMap<String, GlobalLock> map = Maps.newConcurrentMap(); /** * 构造函数 * * @param key */ private GlobalLockRedisImpl(String key) { this.key = "_LOCK_" + key; this.redisService = (RedisService) MyApplicationContext.getBean("redisService"); } public static GlobalLock getInstance(String key) { GlobalLock globalLock = map.get(key); if (globalLock == null) { map.put(key, new GlobalLockRedisImpl(key)); return map.get(key); } return globalLock; } public boolean tryLock() { long result = redisService.increment(key); boolean success = result <= 1; if (!success) {// 锁已被占用,等待释放 result = redisService.increment(key); success = result <= 1; } if (success) {// 处理锁的自动释放 redisService.set(key, String.valueOf(result), TIME_OUT); locked = true; if (logger.isDebugEnabled()) { logger.debug("尝试获取锁{}成功, 锁将在 {} 秒后释放", key, TIME_OUT); } } return success; } public void lock(int timeout) throws RuntimeException { long nanoTime = System.nanoTime(); do { if (redisService.setIfAbsent(key, key)) { redisService.expire(key, 1, TimeUnit.MINUTES); //设定锁过期时间为1分钟 locked = true; if (logger.isDebugEnabled()) { logger.debug("get key: {} lock success! -- {} 毫秒", key, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime)); } return; } try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } while ((System.nanoTime() - nanoTime) < TimeUnit.SECONDS.toNanos(timeout)); throw new RuntimeException("获取锁超时, 消耗时间: " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime) + "毫秒"); } public void lock() throws RuntimeException { lock(TIME_OUT); } public void unlock() { if (locked) { redisService.delete(key); } } }
public void lock() throws GDSystemException {
lock(30);
}
public void lock(int timeout) throws GDSystemException {
long nanoTime = System.nanoTime();
while(!redisService.setIfAbsent(this.key, this.key)) {
try {
Thread.sleep(300L);
} catch (InterruptedException var5) {
var5.printStackTrace();
}
if(System.nanoTime() - nanoTime >= TimeUnit.SECONDS.toNanos((long)timeout)) {
throw new GDSystemException("获取锁超时, 消耗时间: " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime) + "毫秒");
}
}
this.redisService.expire(this.key,60, TimeUnit.SECONDS);
this.locked = true;
if(logger.isDebugEnabled()) {
logger.debug("get key: {} lock success! -- {} 毫秒", new Object[]{this.key, Long.valueOf(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime))});
}
}
lock 默认你的时间30s,
redisService.setIfAbsent(this.key, this.key)
进一步来看setIfAbsent的源码:
public Boolean setIfAbsent(String key, Object value) {
return this.getRedisTemplate().opsForValue().setIfAbsent(key, value);
}
public Boolean setIfAbsent(K key, V value) {
final byte[] rawKey = this.rawKey(key);
final byte[] rawValue = this.rawValue(value);
return (Boolean)this.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.setNX(rawKey, rawValue);
}
}, true);
}
对 Redis setNX的这个方法,参考官方文档,判断key是否存在(1/0)
Return value
Integer reply, specifically:
1 if the key was set
0 if the key was not set
再往下看 redisService.expire 保证key 60s有效,足够执行业务代码,
执行往后,delete该key