分布式锁是控制分布式系统之间同步访问共享资源的一种方式
锁接口定义
定义一个锁通用接口,对外提供锁服务
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 RedisTemplatestringRedisTemplate; /** 锁默认超时时间,单位:秒 */ @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 script = new DefaultRedisScript (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(); }}