在实际的工作中,有部分的特定场景需要使用到分布式锁来进行跨服务器资源的统一调配。之前在一家医疗互联网公司,因为黄牛抢号等原因,造成同一个患者同一时段在同一个医生处,挂到了两个及以上的号,我对之前我司实现的代码进行了封装和改进,在github上提供了源码,有需要的朋友,可以下载代码,并用maven打包成jar包,使用起来比较方便。

源码地址https://github.com/mantuliu/distributed

核心feature:

redis的setnx()方法,此方法提供了一个原子操作,可以保证有且只有一个分布式的调用返回值为1,在分布式锁的概念里,则代表此锁被此次调用的线程占用;

redis的expire()方法,通过此方法来设置此锁的过期时间;

对于死锁的情况,封装的分布式锁包可以自动解锁;

每次获取锁的线程,都会被标识,当此线程还没有释放锁时,此线程继续调用trylock方法,还可以获得该锁,只有获取该锁的线程才有unlock()释放锁的权利;

支持trylock()和trylock(long timeout, TimeUnit unit)两种方式;

使用应用服务器提供的jedis实例;

对于当前服务器试图获取该锁的线程数量进行监控,当数量大于阀值时,后续线程在trylock(long timeout, TimeUnit unit)及trylock()时,直接返回失败,阀值可以设置;

锁的过期时间默认值是5秒,可以根据实际情况进行设置;

锁的键值前缀默认值是mantu:dislock:,可以根据实际情况进行设置;

通过LockSupport类来获取锁,使得试图获取同一把锁的线程得到的对象是同一个。

源码解析:

   CommonType类

public class CommonType {

    public static int WAITLOCKERS = 2;//当前服务器等待锁的线程数量,如果超过或等于此值,当前线程直接返回,不再等待锁
public static String REDISKEY="mantu:dislock:";//redis下key前缀
public static int LOCKEXPIRETIME = 5;//锁的过期时间,单位秒,默认5秒过期
}

DisLock接口

public interface DisLock{

    boolean tryLock(Jedis jedis);
boolean tryLock(long time, TimeUnit unit,Jedis jedis) throws InterruptedException;
void unlock(Jedis jedis);
}

RedisDisLock类,实际的管理锁的类

public class RedisDisLock implements DisLock{
private static final Logger LOG = LoggerFactory.getLogger(RedisDisLock.class);
private transient Thread exclusiveOwnerThread; String lockKey="";
AtomicInteger waitToLock=new AtomicInteger(0); public RedisDisLock(String lockKey){
this.lockKey=CommonType.REDISKEY+lockKey;
} public boolean tryLock(Jedis jedis) {
Thread thread = Thread.currentThread();
if(thread==this.getExclusiveOwnerThread()){
return true;
}
Long i = jedis.setnx(lockKey, System.currentTimeMillis()+"");
if(i.intValue()==1){
jedis.expire(lockKey, CommonType.LOCKEXPIRETIME);
setExclusiveOwnerThread(thread);
return true;
}
else{//对于可能性非常低的死锁情况进行解锁
String initTime = jedis.get(lockKey);
if(initTime==null){
LOG.debug("initTime's value is null");
return false;
}
long iniTime=0L;
try{
iniTime = Long.parseLong(initTime);
}
catch(NumberFormatException nfex){
LOG.warn(nfex.getMessage());
jedis.expire(lockKey, 1);
return false;
}
if(((System.currentTimeMillis()-iniTime)/1000-CommonType.LOCKEXPIRETIME-1)>0){
String oldTime = jedis.getSet(lockKey, System.currentTimeMillis()+"");//对于及其极端的情况,lock被线程1处理掉了,但是又被线程2getset新的值了,通过下一次调用trylock()方法处理
if(oldTime==null){
LOG.info("oldTime is null");
return false;
}
if(initTime.equals(oldTime)){
release(jedis);
}
}
}
return false;
} public boolean tryLock(long timeout, TimeUnit unit,Jedis jedis) throws InterruptedException {
long nanosTimeout = unit.toNanos(timeout);
long lastTime = System.nanoTime();
if(tryLock(jedis)){
return true;
}
try{
int waitLockers = waitToLock.getAndIncrement();
if(waitLockers>=CommonType.WAITLOCKERS){
LOG.debug("wait the lock' thread num is much,so return flase");
return false;
}
for(;;){
if(tryLock(jedis)){
return true;
}
if (nanosTimeout <= 0){
LOG.debug("getlock timeout");
return false;
}
if(nanosTimeout>100000){
LockSupport.parkNanos(100000);//中断100毫秒
}
long now = System.nanoTime();
nanosTimeout -= now - lastTime;
lastTime = now;
if (nanosTimeout <= 0){
LOG.debug("getlock timeout");
return false;
}
if (Thread.interrupted()){
throw new InterruptedException();
}
}
}
finally{
waitToLock.decrementAndGet();
}
} public void unlock(Jedis jedis) {
Thread thread = Thread.currentThread();
if(thread==this.getExclusiveOwnerThread()){
LOG.debug("unlock the thread {}",thread.getId());
release(jedis);
}
} private void release(Jedis jedis){
setExclusiveOwnerThread(null);
jedis.del(lockKey);
} /**
* Sets the thread that currently owns exclusive access. A
* <tt>null</tt> argument indicates that no thread owns access.
* This method does not otherwise impose any synchronization or
* <tt>volatile</tt> field accesses.
*/
protected final void setExclusiveOwnerThread(Thread t) {
exclusiveOwnerThread = t;
} /**
* Returns the thread last set by
* <tt>setExclusiveOwnerThread</tt>, or <tt>null</tt> if never
* set. This method does not otherwise impose any synchronization
* or <tt>volatile</tt> field accesses.
* @return the owner thread
*/
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}

LockSupport类,实际的业务代码首先要通过LockSupport来获取redis锁的对象,再使用

public class LockSupport {

    static ConcurrentHashMap <String,RedisDisLock>lockMap = new ConcurrentHashMap<String,RedisDisLock>();

    public static DisLock getRedisLock(String lockKey){
RedisDisLock lock=null;
if(lockMap.contains(lockKey)){
lock = lockMap.get(lockKey);
}
else{
RedisDisLock lockN = new RedisDisLock(lockKey);
lock = lockMap.putIfAbsent(lockKey, lockN);
if(lock==null){
lock=lockN;
}
}
return lock;
}
}

RedisDisLockTest类是使用此jar的demo代码

public class RedisDisLockTest {

    public static void main(String [] args){
RedisDisLockTest test = new RedisDisLockTest();
//test.testOrder();
//test.testOrder2();
//test.testNOUnlock();
test.testOtherUnlock();
}
public void testOrder(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
OrderThread th = new OrderThread("123456",jedis);
th.start();
}
}
public void testOrder2(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
OrderThread th = new OrderThread("1234567",jedis);
th.start();
}
}
public void testNOUnlock(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
TestNOUnlock th = new TestNOUnlock("12345678",jedis);
th.start();
}
}
public void testOtherUnlock(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
TestOtherUnlock th = new TestOtherUnlock("unlock",jedis);
th.start();
}
}
class OrderThread extends Thread{
String lockKey="";
Jedis jedis;
public OrderThread(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
try {
if(lock.tryLock(2,TimeUnit.SECONDS,jedis)){
System.out.println("订单"+lockKey+"创建成功!");
lock.unlock(jedis);
}
else{
System.out.println("没有成功获取到锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} class TestNOUnlock extends Thread{
String lockKey="";
Jedis jedis;
public TestNOUnlock(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
try {
if(lock.tryLock(2,TimeUnit.SECONDS,jedis)){
System.out.println("订单"+lockKey+"创建成功!");
//lock.unlock(jedis);//no unlock
}
else{
System.out.println("没有成功获取到锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} class TestOtherUnlock extends Thread{
String lockKey="";
Jedis jedis;
public TestOtherUnlock(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
if(lock.tryLock(jedis)){
System.out.println("订单"+lockKey+"创建成功!");
//lock.unlock(jedis);//no unlock
}
else{
lock.unlock(jedis);
System.out.println("TestOtherUnlock没有成功获取到锁");
} }
}
}

  

使用redis来实现分布式锁的更多相关文章

  1. 基于redis实现的分布式锁

    基于redis实现的分布式锁 我们知道,在多线程环境中,锁是实现共享资源互斥访问的重要机制,以保证任何时刻只有一个线程在访问共享资源.锁的基本原理是:用一个状态值表示锁,对锁的占用和释放通过状态值来标 ...

  2. 一个Redis实现的分布式锁

    import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.conne ...

  3. 基于Redis的简单分布式锁的原理

    参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的 ...

  4. redis客户端、分布式锁及数据一致性

    Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...

  5. Redis系列(二)--分布式锁、分布式ID简单实现及思路

    分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...

  6. 在redis上实现分布式锁

    /** *在redis上实现分布式锁 */ class RedisLock { private $redisString; private $lockedNames = []; public func ...

  7. 如何用redis正确实现分布式锁?

    先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般r ...

  8. redis系列:分布式锁

    redis系列:分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...

  9. 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?

    #(1)redis分布式锁 官方叫做RedLock算法,是redis官方支持的分布式锁算法. 这个分布式锁有3个重要的考量点,互斥(只能有一个客户端获取锁),不能死锁,容错(大部分redis节点创建了 ...

  10. Redis如何实现分布式锁

    今天我们来聊一聊分布式锁的那些事. 相信大家对锁已经不陌生了,我们在多线程环境中,如果需要对同一个资源进行操作,为了避免数据不一致,我们需要在操作共享资源之前进行加锁操作.在计算机科学中,锁(lock ...

随机推荐

  1. 24种设计模式--适配器模式【Adapter Pattern】

    今天讲适配器模式,这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你在中国能用,在日本也能用,虽然两个国家的的电源电压不同,中国是 220V,日本是 110V,但是这个适配器能够把 ...

  2. window scipy install

    http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy whl包,使用pip install xx.whl  安装 1:先安装 numpy+mkl.  whl ...

  3. #Leet Code# LRU Cache

    语言:C++ 描述:使用单链表实现,HeadNode是key=-1,value=-1,next=NULL的结点.距离HeadNode近的结点是使用频度最小的Node. struct Node { in ...

  4. Struts2技术内幕-----第七章

    1)基于人机交互的请求--响应模式主要由哪三大要素构成?     ①沟通协议-----人和机器都能够明白的数据通信格式     ②请求内容-----人通过某种机制向机器发起的数据请求     ③响应内 ...

  5. Mac os 10.9下面配置JAVA_HOME

    刚入手的的MBP,就开始配置java环境,搜了一下网上的都是10.9以前的配置方法.jdk7在10.9的安装目录变化了. 首先到Oracle官网下载最新版本的java,直接默认安装 cd /etc s ...

  6. python之requests-multipart/from-data

    示例代码:files = {"token":(None,token), "key":(None,key), "file":"hel ...

  7. 高性能页面加载技术(流水线加载)BigPipe的C#简单实现(附源码)

    一,BigPipe简介 BigPipe是一个重新设计的基础动态网页服务体系.大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器和浏览器建立管道并管理他们在不同阶段的运行.这是类似于 ...

  8. Jquery的attr属性

    在JS中设置节点的属性与属性值用到setAttribute(),获得节点的属性与属性值用到getAttribute(),而在jquery中,用一个attr()就可以全部搞定了,赞一个先 ^^ jque ...

  9. 【UVALive - 3713】Astronauts (2-SAT)

    题意: 有n个宇航员,按照年龄划分,年龄低于平均年龄的是年轻宇航员,而年龄大于等于平均年龄的是老练的宇航员. 现在要分配他们去A,B,C三个空间站,其中A站只有老练的宇航员才能去,而B站是只有年轻的才 ...

  10. [topcoder]CoinReversing

    http://community.topcoder.com/stat?c=problem_statement&pm=11473&rd=14543 简单的概率题.那道题想了想就出来了.每 ...