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

}
关注微信公众号

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com