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