基于Redis实现分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式

锁接口定义

定义一个锁通用接口,对外提供锁服务

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();
    }

}

原文地址:https://www.cnblogs.com/coderzl/p/7490226.html