基于单点redis服务的分布式锁简单实现
2017-11-14 14:32
766 查看
由于项目中有需要需要加锁的方法,而项目的pc和和移动端又在不同的服务器里。由于项目处于不同的jvm中,jdk自带的锁机制无法达到效果了。故看了一些基于redis实现分布式锁的方式,总结如下:
分布式锁可以基于很多种方式实现,比如zookeeper、redis…。基本原理是相通的:用一个状态值表示锁,对锁的占用和释放通过状态值来标识。
在 Redis 里,有一个 SETNX函数,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。
利用该函数的特性,即如果key已经存在,setnx返回1,如果key已经存在,setnx返回0。通过返回值来判断能否将key插入到redis中。
为了避免因为某一个线程意外挂起或因故无法释放锁的问题,需要为锁增加一个有效时间概念。redis的expire()函数来给key设置过期时间,当各种原因未能对锁unlock时,可以通过过期时间来实现锁的释放。
如下是实现分布式锁的及测试代码(实际应用中应注意timeout和expireSecs的值设定):
1.分布式锁实现
2.测试代码
分布式锁可以基于很多种方式实现,比如zookeeper、redis…。基本原理是相通的:用一个状态值表示锁,对锁的占用和释放通过状态值来标识。
在 Redis 里,有一个 SETNX函数,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。
利用该函数的特性,即如果key已经存在,setnx返回1,如果key已经存在,setnx返回0。通过返回值来判断能否将key插入到redis中。
为了避免因为某一个线程意外挂起或因故无法释放锁的问题,需要为锁增加一个有效时间概念。redis的expire()函数来给key设置过期时间,当各种原因未能对锁unlock时,可以通过过期时间来实现锁的释放。
如下是实现分布式锁的及测试代码(实际应用中应注意timeout和expireSecs的值设定):
1.分布式锁实现
import org.apache.commons.lang3.StringUtils; import redis.clients.jedis.Jedis; public class RedisLock { private RedisLock redisLock; /** 毫秒与毫微秒的换算单位 1毫秒 = 1000000毫微秒 */ private static final long MILLI_NANO_CONVERSION = 1000 * 1000L; private static final String EXPIRE_KEY_PREFIX = "lock:%s"; private boolean locked; private String key; public RedisLock(String key) { if (StringUtils.isEmpty(key)) { throw new IllegalArgumentException("Param key can not be null or empty"); } this.key = String.format(EXPIRE_KEY_PREFIX, key); this.locked = false; redisLock = this; } /** * 获取锁 * 在timeout时间内会一直尝试获取锁,如果timeout=0,则表示获取失败后直接返回不再尝试 * 锁的有效期expireSecs必须大于0,当超过有效期未被unlock时,系统将会强制释放 * @param timeout 获取锁的等待时间 * @param expireSecs 锁的有效时间,必须大于0 * @return */ public boolean lock(long timeout, int expireSecs) { if (expireSecs <= 0) { throw new IllegalArgumentException("Param expireSecs must lager than zero."); } long nano = System.nanoTime(); timeout *= MILLI_NANO_CONVERSION; Jedis jedis = new Jedis("127.0.0.1", 6379);//连接redis服务器,127.0.0.1:6379 try { while ((System.nanoTime() - nano) < timeout) { String lockStart = String.valueOf(System.currentTimeMillis()); if (jedis.setnx(this.key, lockStart) == 1) { jedis.expire(this.key, expireSecs); this.locked = true; return this.locked; } Thread.sleep(30); } String expireStr = jedis.get(key); Long now = System.currentTimeMillis(); String nowStr = String.valueOf(now); if (!StringUtils.isNumeric(expireStr)) { return false; } //ttl小于0 表示key上没有设置生存时间(key是不会不存在的,因为前面setnx会自动创建) //出现这种状况,是因为某个实例setnx成功后,expire由于各种可能原因而没有被调用造成的 //这时可以直接设置expire来占有锁 long ttlValue = jedis.ttl(this.key); if(ttlValue<0){ jedis.setnx(key, nowStr); jedis.expire(key, expireSecs); System.out.println("key上没有设置生存时间,获得锁"); return true; } Long expireLong = Long.parseLong(expireStr); //若锁超时,则直接删除key if (now - expireLong > expireSecs * 1000) { jedis.del(key); jedis.setnx(key, nowStr); jedis.expire(key, expireSecs); System.out.println("锁超时:"+(now - expireLong)+",获得锁"); return true; } } catch (Exception e) { throw new RuntimeException("Locking error", e); } return false; } /** * 释放锁 * @return */ public boolean unLock() { Jedis jedis = new Jedis("127.0.0.1", 6379);//连接redis服务器,127.0.0.1:6379 try { if (jedis.del(this.key) == 1) { this.locked = false; return true; } return false; } catch (Exception e) { throw new RuntimeException("UnLocking error", e); } } }
2.测试代码
import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class RedisLockTest { //等待时间 private static final int timeout=25*1000; //锁过期时间 private static final int expireSecs=10; public static void main(String[] args) { RedisLock yuxinLock = new RedisLock("yuxin"); new RedisLockTest().testM(yuxinLock); } public void testM(RedisLock yuxinLock){ ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); for(int i = 0; i < 5; i++){ fixedThreadPool.execute(new Task("woker_" + i,yuxinLock)); } } class Task implements Runnable{ private String name; private RedisLock yuxinLock; public Task(String name,RedisLock yuxinLock){ this.name = name; this.yuxinLock=yuxinLock; System.out.println(name); } public void run() { try{ System.out.println(name+"开始夺锁大战"); if(yuxinLock.lock(timeout,expireSecs)){ System.out.println(this.name + "开始工作了"+new Date()); int time = 5000; if(time > 0){ Thread.sleep(time); } System.out.println(this.name + "结束工作了;" + new Date()); }else{ System.out.println(name+"等待超时"); } } catch (InterruptedException e) { e.printStackTrace(); } finally{ yuxinLock.unLock(); } } } }
相关文章推荐
- 基于Redis的分布式锁的简单实现
- 2种基于简单定时任务实现分布式定时任务的技术解决方案(zookeeper、redis和内联MQ)
- 基于redis的分布式锁服务实现
- 基于Redis实现简单的分布式锁
- 简单实现了一个基于redis的分布式锁,存在bug...
- 基于Redis实现简单的分布式锁
- 基于Redis实现简单的分布式锁
- Spring Boot 实践3 --基于spring cloud 实现微服务的简单调用
- Dubbo+Zookeeper+Spring整合应用篇-Dubbo基于Zookeeper实现分布式服务
- 基于Redis实现分布式锁
- Redis实现简单版本分布式锁
- 基于Redis实现延时队列服务
- Spark生态系统解析及基于Redis的开源分布式服务Codis
- 基于Redis实现分布式锁
- 分布式服务框架远程服务通讯简单实现
- 基于Spring-DM实现分布式服务框架(DSF)(一)
- Golang基于redis实现的分布式信号量(semaphore)
- 通过Redisson实现基于redis的分布式锁
- Java实现基于Redis的分布式锁
- 采用Best effort 1pc + 回滚补偿机制实现的一个distributed transaction (分布式事务框架).基于dubbo rpc服务上实现。