分布式锁 基于Redis
分布式锁的实现(基于Redis)
参考:http://www.jb51.net/article/75439.htm
http://www.linuxidc.com/Linux/2015-01/111827.htm
http://www.tuicool.com/articles/6juqmm7
方式一: 基于第三方类库 redssion
1.安装redis
安装redssion的锁服务队redis的版本有要求,要求必须高于2.6版本。关于redis的安装,请参考redis安装。
2.redssion库
redssion的库添加的pom文件中去。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.1.0</version>
</dependency>
3.测试例子
Config config = new Config();
config.useSingleServer().setAddress("ipaddress:6379");
Redisson redisson = Redisson.create(config);
for(int i=0;i<5;i++){
RLock lock = redisson.getLock("testLock");
lock.lock(10, TimeUnit.SECONDS);//10s超时
logger.info("the lock client id is client{}",i);
Thread.sleep(1000);
logger.info("client{} unlock",i);
lock.unlock();
}
4.参考
方式二: 自定义代码实现
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
/**
* redis实现的分布式锁
*/
public final class Lock {
private static final String LOCK = "redis:lock:%s";
private static final int EXPIRE = 20 * 60;//20分钟
private static final Logger logger = LoggerFactory.getLogger(Lock.class);
private Lock() {
}
public static Lock getLock() {
return new Lock();
}
/**
* 获得锁,超时退出
* @param id
* @param timeout 超时时间(ms)
* @return
*/
public boolean lock(String id, int timeout) {
//这里的Jedis实际上是个代理。代理类JedisCallback,JedisFactoryBean
Jedis jedisProxy = JedisProxy.create();
long lock = 0;
long start = System.currentTimeMillis();
while (lock != 1) {
long now = System.currentTimeMillis();
//判断超时
if (timeout > 0 && now > start + timeout) {
return false;
}
long timestamp = now + EXPIRE * 1000 + 1;
try {
String key = String.format(LOCK, id);
lock = jedisProxy.setnx(key, String.valueOf(timestamp));
if (lock == 1) {
logger.info("设置redis锁key成功,lock key=" + key);
jedisProxy.expire(key, EXPIRE);
logger.info("设置redis锁key过期时间成功,lock key=" + key);
} else {
String s = jedisProxy.get(key);
Long lastLockTime = Long.parseLong(s);
//一个项目部署多个服务,锁可能已经被相同定时任务的其他进程获得,则直接返回false
if (jedisProxy.ttl(key)!=-1&&lastLockTime > System.currentTimeMillis()){
logger.info("redis锁已经被其他服务获得,lock key=" + key);
return false;
}
/**
* 如果上次锁定的时间已经过期,则清除key锁,以便定时任务能够启动
* 造成未能释放的原因主要是jedis.expire(key, EXPIRE);失败
* 或者是获取锁之后,由于程序错误或网络错误,unlock未被成功调用
*/
if (jedisProxy.ttl(key)==-1||lastLockTime < System.currentTimeMillis()) {
jedisProxy.del(key);
continue;
}
// this.wait(100);
Thread.sleep(100);
}
} catch (Exception e) {
logger.error("从redis获取定时任务锁错误,key="+String.format(LOCK, id), e);
return false;
}
}
return true;
}
/**
* 释放锁
* @param id
*/
public void unLock(String id) {
//这里的Jedis实际上是个代理。代理类JedisCallback,JedisFactoryBean
Jedis jedisProxy = JedisProxy.create();
String key = String.format(LOCK, id);
jedisProxy.del(key);
}
}
JedisProxy类
/**
* Jedis代理
*/
@Component
public class JedisProxy implements ApplicationContextAware {
private static volatile ApplicationContext ac;
/**
* 创建Jedis 代理
*
* @return
*/
public static Jedis create() {
return ac.getBean(Jedis.class);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (ac != null) {
return;
}
synchronized (JedisProxy.class) {
if (ac != null) {
return;
}
ac = applicationContext;
}
}
}