基于Redis的分布式锁

1.分布式锁的作用

在分布式的系统中,系统之间的不同线程对共享变量的操作无法通过同步锁实现,所以可以利用分布式锁控制多个进程对资源的访问

如果是单机情况下(单JVM),线程之间共享内存,只要使用线程锁就可以解决并发问题。

如果是分布式情况下(多JVM),线程A和线程B很可能不是在同一JVM中,这样线程锁就无法起到作用了,这时候就要用到分布式锁来解决。

2.分布式锁的实现前提

redis 内置lua解析器解析lua脚本,利用脚本的原子性实现分布式锁

好处:lua脚本来解决一些需要保证原子性的问题,而且lua脚本可以缓存在redis服务器上,势必会增加性能。 

方法:eval(script numkeys key [key ...] arg [arg ...])

第一个参数是:lua脚本程序,它运行在redis服务器中

第二个参数是:表示参数的个数

第三个参数是:表示脚本所用到的redis键(key),可以多个,在lua脚本中可以通过KEY[n],如:KEY[1],KEY[2]…来访问这些参数

第四个参数是:表示脚本运行的所需的参数,在lua脚本中可以通过ARGV[n],如ARGV[1]…代表这些参数

3.分布式锁的代码实现


import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.JedisCluster;

import com.google.common.collect.Lists;
import com.google.common.io.CharStreams;

public class RedisDistributedLock implements DistributedLock {

    private String lockName;

    private String identifier;//任意唯一的值如uuid

    private volatile static String acquireLockLua;

    private volatile static String releaseLockLua;

    private static final Logger LOG = LoggerFactory.getLogger(RedisDistributedLock.class);

    private void init() {
        synchronized (RedisDistributedLock.class) {
            try {
                if (null == acquireLockLua) {
                    acquireLockLua = loadLuaScriptString("RedisAcquireLock.lua");
                }

                if (null == releaseLockLua) {
                    releaseLockLua = loadLuaScriptString("RedisReleaseLock.lua");
                }

            } catch (IOException e) {
                LOG.error("基于redis分布式锁获取lua脚本失败", e);
            }
        }
    }

    public RedisDistributedLock(String lockName) {
        this.lockName = lockName;
    }

    @Override
    public boolean tryAcquire() {
        if (null == acquireLockLua) {
            init();
        }
        identifier = UUIDUtil.getConCurrentUUID();

        JedisCluster jedisCluster = RedisUtil.getJedisCluster();
        String result = (String) jedisCluster.eval(acquireLockLua, Lists.newArrayList(lockName),
                Lists.newArrayList("20", identifier));
        return "OK".equals(result);
    }

    @Override
    public boolean release() {
        if (null == releaseLockLua) {
            init();
        }
        JedisCluster jedisCluster = RedisUtil.getJedisCluster();
        Long result = (Long) jedisCluster.eval(releaseLockLua, Lists.newArrayList(lockName),
                Lists.newArrayList(identifier));
        return 1 == result;
    }

    private String loadLuaScriptString(String fileName) throws IOException {
        Reader reader = null;
        String luaStr = null;
        try {
            reader = new InputStreamReader(Semaphore4Redis.class.getResourceAsStream(fileName));
            luaStr = CharStreams.toString(reader);
        } finally {
            if (null != reader) {
                reader.close();
            }
        }
        return luaStr;
    }
}

接口定义:

public interface DistributedLock {
    public boolean tryAcquire();

    public boolean release();
}

lua脚本:

RedisAcquireLock.lua

if redis.call('exists', KEYS[1]) == 0 then
	return redis.call('setex', KEYS[1], unpack(ARGV))
end

RedisReleaseLock.lua

if redis.call('get', KEYS[1]) == ARGV[1] then
	return redis.call('del', KEYS[1]) or true
else
	return true;
end

官方关于redis分布式锁的更多参照如下:

http://www.redis.cn/topics/distlock.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值