分布式锁是控制分布式系统之间同步访问共享资源的一种方式
锁接口定义
定义一个锁通用接口,对外提供锁服务
import java.util.concurrent.TimeUnit;
public interface LockService {
/**
* 尝试获取锁
* @param 锁名
*/
public boolean tryLock(String name);
/**
* 在指定时间内尝试获取锁
* @param 锁名
* @param 尝试时间
*/
public boolean tryLock(String name, long timeout, TimeUnit unit);
/**
* 获取锁,阻塞方法,直到获取锁成功为止
* @param 锁名
*/
public void lock(String name) throws Exception;
/**
* 释放锁
* @param 锁名
*/
public void unlock(String name);
}
redis锁的实现:
public class RedisLockService implements LockService {
private static final String NAMESPACE = "lock-service:";
private static final Logger log = LogManager.getLogger(RedisLockService.class);
@Autowired
private RedisTemplate<String, String> stringRedisTemplate;
/** 锁默认超时时间,单位:秒 */
@Value("${lockService.timeout:300}")
private int timeout;
/** 服务ID,用于区分释放锁权限。 */
@Value("${instance.id:locks}")
private String instanceId;
@Override
public boolean tryLock(String name) {
String key = NAMESPACE + name;
String value = getValue();
boolean success = stringRedisTemplate.opsForValue().setIfAbsent(key, value); //如果不存在key,则set
if (success)
stringRedisTemplate.expire(key, this.timeout, TimeUnit.SECONDS); //设置默认锁有效时间
return success;
}
@Override
public boolean tryLock(String name, long timeout, TimeUnit unit) {
if (timeout <= 0)
return tryLock(name);
String key = NAMESPACE + name;
String value = getValue();
boolean success = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
long millisTimeout = unit.toMillis(timeout);
long start = System.currentTimeMillis();
while (!success) { //若获取锁不成功,则在限定时间内不断尝试,直到获取成功,或超时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return false;
}
if ((System.currentTimeMillis() - start) >= millisTimeout)
break;
success = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
}
if (success)
stringRedisTemplate.expire(key, this.timeout, TimeUnit.SECONDS);
return success;
}
@Override
public void lock(String name) {
String key = NAMESPACE + name;
String value = getValue();
boolean success = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
while (!success) {
try {
Thread.sleep(100); //每间隔100毫秒,再次尝试获取锁
} catch (InterruptedException e) {
e.printStackTrace();
}
success = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
if (success)
stringRedisTemplate.expire(key, this.timeout, TimeUnit.SECONDS);
}
}
@Override
public void unlock(String name) {
String key = NAMESPACE + name;
String value = getValue();
//一行lua脚本,如果KEY的值等于VALUE,则删除KEY,否则返回0
String str = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";
RedisScript<Long> script = new DefaultRedisScript<Long>(str, Long.class);
Long ret = stringRedisTemplate.execute(script, Collections.singletonList(key), value);
if (ret == 0)
log.warn("Lock [{}] is not hold by instance [{}]", name, value);
}
/**
* 生成由服务ID+线程名组成的线程唯一value。用于保证只能被拥有锁的线程解锁
*/
private String getValue(){
return instanceId + Thread.currentThread().getName();
}
}