分布式鎖的實現(基于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.參考
redisson
?
?
方式二: 自定義代碼實現?
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;
}
}
}
?
?
?
?
?
?