在项目开发中,常常会碰到需要做lock锁的操作,而Redis又是最常用的分布式锁。在这提供一种简单的分布式锁实现,当然这里也有些考虑不周到,比如没实现公平与非公平锁、重入特性以及setNx及expire命令是分开的在极端(比如执行完A命令程序被kill -9了)造成死锁情况,没做redis集群情况下的高可用。
@Component
public class RedisOperation {
@Autowired
private RedisTemplate redisTemplate;
private static final long DEFAULT_EXPIRATION_TIMES = 600l;
public void set(final String key, final String value) {
this.set(key, value, DEFAULT_EXPIRATION_TIMES);
}
public void set(final String key, final String value, final long seconds) {
redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.setEx(redisTemplate.getStringSerializer().serialize(key), seconds, redisTemplate.getStringSerializer().serialize(value));
return null;
}
});
}
public boolean setNx(final String key, final String value, final long seconds) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
byte[] serializeKey = redisTemplate.getStringSerializer().serialize(key);
boolean result = connection.setNX(serializeKey, redisTemplate.getStringSerializer().serialize(value));
if (result) {
connection.expire(serializeKey, seconds);
}
return result;
}
});
}
public String get(final String key) {
return redisTemplate.execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
byte[] byteKye = redisTemplate.getStringSerializer().serialize(key);
if (connection.exists(byteKye)) {
byte[] byteValue = connection.get(byteKye);
String value = redisTemplate.getStringSerializer().deserialize(byteValue);
return value;
}
return null;
}
});
}
public boolean exists(final String key) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.exists(redisTemplate.getStringSerializer().serialize(key));
}
});
}
public void delete(final String key) {
redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.del(redisTemplate.getStringSerializer().serialize(key));
return null;
}
});
}
}
@Component
public class DistributeLock {
private static final Logger logger = LoggerFactory.getLogger(DistributeLock.class);
public static final String REDIS_LOCK = "Demo:RedisLock:";
@Autowired
private RedisOperation redisOperation;
public boolean lock(String key, long timeout, long waitTimeout) {
return lock(key, timeout, waitTimeout, true);
}
/**
*
* @param key
* @param timeout key超时时间 单位:秒
* @param waitTimeout 等待获取锁时间 单位:毫秒
* @param force 是否强依赖锁
* @return
*
* @author hz15041240
* @date 2017-8-30 下午5:20:14
* @version
*/
public boolean lock(String key, long timeout, long waitTimeout, boolean forced) {
String lockKey = generateLockKey(key);
long nanoWaitForLock = TimeUnit.MILLISECONDS.toNanos(waitTimeout);
long start = System.nanoTime();
try {
do {
if (redisOperation.setNx(lockKey, key, timeout)) {
logger.debug("add distributed lock succeed; lockKey:{}", lockKey);
return true;
}
TimeUnit.MILLISECONDS.sleep(RandomUtils.nextInt(10, 80));
} while ((System.nanoTime() - start) < nanoWaitForLock);
} catch (Exception e) {
logger.error(String.format("add distributed lock exception lockKey:%s", lockKey), e);
unlock(lockKey);
return !forced;
}
return false;
}
public void unlock(String key) {
String lockKey = generateLockKey(key);
try {
redisOperation.delete(lockKey);
logger.debug("remove distributed lock key:{} succeed", lockKey);
} catch (Exception e) {
logger.error(String.format("unlock exception lockKey:{} ", lockKey), e);
}
}
public boolean isLock(String key) {
String lockKey = generateLockKey(key);
return redisOperation.exists(lockKey);
}
private String generateLockKey(String key) {
return new StringBuilder(50).append(REDIS_LOCK).append(key).toString();
}
}