文档章节

jedis实现分布式锁(悲观锁)

y
 yiqifendou
发布于 2016/10/08 15:27
字数 1176
阅读 82
收藏 0
点赞 0
评论 0

redis需单点部署(非集群)
redis分布式锁解决多个应用进程间同步操作

整理了很多网上文档 发现都没有解决如下问题。。。

参考

http://www.cnblogs.com/it-cen/p/4984272.html

...

1.时间同步问题

2.在一个进程cash后失效时间后自动释放锁

3.有些多线程race condition没有考虑到

以下java版本实现彻底解决 充分测试

** 方案一(推荐)**


import java.util.List;
import java.util.UUID;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;

/**
 * Jedis实现分布式锁
 * 
 * @author 三文鱼
 *
 */
public class DistributionLock {
    private final JedisPool jedisPool;

    public DistributionLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 获取分布式锁
     * 
     * @param lockName
     *            竞争获取锁key
     * @param acquireTimeoutInMS
     *            获取锁超时时间
     * @param lockTimeoutInMS
     *            锁的超时时间
     * @return 获取锁标识
     */
    public String acquireLockWithTimeout(String lockName,
            long acquireTimeoutInMS, long lockTimeoutInMS) {
        Jedis conn = null;
        boolean broken = false;
        String retIdentifier = null;
        try {
            conn = jedisPool.getResource();
            String identifier = UUID.randomUUID().toString();
            String lockKey = "lock:" + lockName;
            int lockExpire = (int) (lockTimeoutInMS / 1000);

            long end = System.currentTimeMillis() + acquireTimeoutInMS;
            while (System.currentTimeMillis() < end) {
                if (conn.setnx(lockKey, identifier) == 1) {
                    conn.expire(lockKey, lockExpire);
                    retIdentifier = identifier;
                }
                if (conn.ttl(lockKey) == -1) {
                    conn.expire(lockKey, lockExpire);
                }

                try {
                    Thread.sleep(10);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException je) {
            if (conn != null) {
                broken = true;
                jedisPool.returnBrokenResource(conn);
            }
        } finally {
            if (conn != null && !broken) {
                jedisPool.returnResource(conn);
            }
        }
        return retIdentifier;
    }

    /**
     * 释放锁
     * @param lockName 竞争获取锁key
     * @param identifier 释放锁标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null;
        boolean broken = false;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            conn = jedisPool.getResource();
            while (true) {
                conn.watch(lockKey);
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction trans = conn.multi();
                    trans.del(lockKey);
                    List<Object> results = trans.exec();
                    if (results == null) {
                        continue;
                    }
                    retFlag = true;
                }
                conn.unwatch();
                break;
            }

        } catch (JedisException je) {
            if (conn != null) {
                broken = true;
                jedisPool.returnBrokenResource(conn);
            }
        } finally {
            if (conn != null && !broken) {
                jedisPool.returnResource(conn);
            }
        }
        return retFlag;
    }

}

**

方案二(不完善,比如unlock没有做所有者校验等,如果误删其他线程持有的锁也会造成问题)**

/** 
 * @author http://blog.csdn.net/java2000_wl 
 * @version <b>1.0.0</b> 
 */  
public class RedisBillLockHandler implements IBatchBillLockHandler {  
  
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisBillLockHandler.class);  
  
    private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3;  
      
    private static final int DEFAULT_BATCH_EXPIRE_TIME = 6;  
  
    private final JedisPool jedisPool;  
      
    /** 
     * 构造 
     * @author http://blog.csdn.net/java2000_wl 
     */  
    public RedisBillLockHandler(JedisPool jedisPool) {  
        this.jedisPool = jedisPool;  
    }  
  
    /** 
     * 获取锁  如果锁可用   立即返回true,  否则返回false 
     * @author http://blog.csdn.net/java2000_wl 
     * @param billIdentify 
     * @return 
     */  
    public boolean tryLock(IBillIdentify billIdentify) {  
        return tryLock(billIdentify, 0L, null);  
    }  
  
    /** 
     * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false 
     * @author http://blog.csdn.net/java2000_wl 
     * @param billIdentify 
     * @param timeout 
     * @param unit 
     * @return 
     */  
    public boolean tryLock(IBillIdentify billIdentify, long timeout, TimeUnit unit) {  
        String key = (String) billIdentify.uniqueIdentify();  
        Jedis jedis = null;  
        try {  
            jedis = getResource();  
            long nano = System.nanoTime();  
            do {  
                LOGGER.debug("try lock key: " + key);  
                Long i = jedis.setnx(key, key);  
                if (i == 1) {   
                    jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);  
                    LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");  
                    return Boolean.TRUE;  
                } else { // 存在锁  
                    if (LOGGER.isDebugEnabled()) {  
                        String desc = jedis.get(key);  
                        LOGGER.debug("key: " + key + " locked by another business:" + desc);  
                    }  
                }  
                if (timeout == 0) {  
                    break;  
                }  
                Thread.sleep(300);  
            } while ((System.nanoTime() - nano) < unit.toNanos(timeout));  
            return Boolean.FALSE;  
        } catch (JedisConnectionException je) {  
            LOGGER.error(je.getMessage(), je);  
            returnBrokenResource(jedis);  
        } catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        } finally {  
            returnResource(jedis);  
        }  
        return Boolean.FALSE;  
    }  
  
    /** 
     * 如果锁空闲立即返回   获取失败 一直等待 
     * @author http://blog.csdn.net/java2000_wl 
     * @param billIdentify 
     */  
    public void lock(IBillIdentify billIdentify) {  
        String key = (String) billIdentify.uniqueIdentify();  
        Jedis jedis = null;  
        try {  
            jedis = getResource();  
            do {  
                LOGGER.debug("lock key: " + key);  
                Long i = jedis.setnx(key, key);  
                if (i == 1) {   
                    jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);  
                    LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");  
                    return;  
                } else {  
                    if (LOGGER.isDebugEnabled()) {  
                        String desc = jedis.get(key);  
                        LOGGER.debug("key: " + key + " locked by another business:" + desc);  
                    }  
                }  
                Thread.sleep(300);   
            } while (true);  
        } catch (JedisConnectionException je) {  
            LOGGER.error(je.getMessage(), je);  
            returnBrokenResource(jedis);  
        } catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        } finally {  
            returnResource(jedis);  
        }  
    }  
  
    /** 
     * 释放锁 
     * @author http://blog.csdn.net/java2000_wl 
     * @param billIdentify 
     */  
    public void unLock(IBillIdentify billIdentify) {  
        List<IBillIdentify> list = new ArrayList<IBillIdentify>();  
        list.add(billIdentify);  
        unLock(list);  
    }  
  
    /** 
     * 批量获取锁  如果全部获取   立即返回true, 部分获取失败 返回false 
     * @author http://blog.csdn.net/java2000_wl 
     * @date 2013-7-22 下午10:27:44 
     * @param billIdentifyList 
     * @return 
     */  
    public boolean tryLock(List<IBillIdentify> billIdentifyList) {  
        return tryLock(billIdentifyList, 0L, null);  
    }  
      
    /** 
     * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false 
     * @author http://blog.csdn.net/java2000_wl 
     * @param billIdentifyList 
     * @param timeout 
     * @param unit 
     * @return 
     */  
    public boolean tryLock(List<IBillIdentify> billIdentifyList, long timeout, TimeUnit unit) {  
        Jedis jedis = null;  
        try {  
            List<String> needLocking = new CopyOnWriteArrayList<String>();    
            List<String> locked = new CopyOnWriteArrayList<String>();     
            jedis = getResource();  
            long nano = System.nanoTime();  
            do {  
                // 构建pipeline,批量提交  
                Pipeline pipeline = jedis.pipelined();  
                for (IBillIdentify identify : billIdentifyList) {  
                    String key = (String) identify.uniqueIdentify();  
                    needLocking.add(key);  
                    pipeline.setnx(key, key);  
                }  
                LOGGER.debug("try lock keys: " + needLocking);  
                // 提交redis执行计数  
                List<Object> results = pipeline.syncAndReturnAll();  
                for (int i = 0; i < results.size(); ++i) {  
                    Long result = (Long) results.get(i);  
                    String key = needLocking.get(i);  
                    if (result == 1) {  // setnx成功,获得锁  
                        jedis.expire(key, DEFAULT_BATCH_EXPIRE_TIME);  
                        locked.add(key);  
                    }   
                }  
                needLocking.removeAll(locked);  // 已锁定资源去除  
                  
                if (CollectionUtils.isEmpty(needLocking)) {  
                    return true;  
                } else {      
                    // 部分资源未能锁住  
                    LOGGER.debug("keys: " + needLocking + " locked by another business:");  
                }  
                  
                if (timeout == 0) {   
                    break;  
                }  
                Thread.sleep(500);    
            } while ((System.nanoTime() - nano) < unit.toNanos(timeout));  
  
            // 得不到锁,释放锁定的部分对象,并返回失败  
            if (!CollectionUtils.isEmpty(locked)) {  
                jedis.del(locked.toArray(new String[0]));  
            }  
            return false;  
        } catch (JedisConnectionException je) {  
            LOGGER.error(je.getMessage(), je);  
            returnBrokenResource(jedis);  
        } catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        } finally {  
            returnResource(jedis);  
        }  
        return true;  
    }  
  
    /** 
     * 批量释放锁 
     * @author http://blog.csdn.net/java2000_wl 
     * @param billIdentifyList 
     */  
    public void unLock(List<IBillIdentify> billIdentifyList) {  
        List<String> keys = new CopyOnWriteArrayList<String>();  
        for (IBillIdentify identify : billIdentifyList) {  
            String key = (String) identify.uniqueIdentify();  
            keys.add(key);  
        }  
        Jedis jedis = null;  
        try {  
            jedis = getResource();  
            jedis.del(keys.toArray(new String[0]));  
            LOGGER.debug("release lock, keys :" + keys);  
        } catch (JedisConnectionException je) {  
            LOGGER.error(je.getMessage(), je);  
            returnBrokenResource(jedis);  
        } catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        } finally {  
            returnResource(jedis);  
        }  
    }  
      
    /** 
     * @author http://blog.csdn.net/java2000_wl 
     * @date 2013-7-22 下午9:33:45 
     * @return 
     */  
    private Jedis getResource() {  
        return jedisPool.getResource();  
    }  
      
    /** 
     * 销毁连接 
     * @author http://blog.csdn.net/java2000_wl 
     * @param jedis 
     */  
    private void returnBrokenResource(Jedis jedis) {  
        if (jedis == null) {  
            return;  
        }  
        try {  
            //容错  
            jedisPool.returnBrokenResource(jedis);  
        } catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        }  
    }  
      
    /** 
     * @author http://blog.csdn.net/java2000_wl 
     * @param jedis 
     */  
    private void returnResource(Jedis jedis) {  
        if (jedis == null) {  
            return;  
        }  
        try {  
            jedisPool.returnResource(jedis);  
        } catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        }  
    }  

本文转载自:http://www.cnblogs.com/wuhuajun/p/5242644.html

共有 人打赏支持
y
粉丝 5
博文 51
码字总数 8974
作品 0
南京
经典:Redis分布式锁的正确实现方式

分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。虽然网上已经有各种介绍Redis分布...

架构之家 ⋅ 01/14 ⋅ 0

Redis 分布式锁的正确实现方式( Java 版 )

原文出处:吴兆锋 前言 分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。虽然网上已...

吴兆锋 ⋅ 2017/12/02 ⋅ 0

使用数据库悲观锁实现不可重入的分布式锁

一、前言 在同一个jvm进程中时,可以使用JUC提供的一些锁来解决多个线程竞争同一个共享资源时候的线程安全问题,但是当多个不同机器上的不同jvm进程共同竞争同一个共享资源时候,juc包的锁就...

阿里加多 ⋅ 06/12 ⋅ 0

Redis实现分布式锁相关注意事项

Redis实现分布式锁相关注意事项 查看了不少关于redis实现分布式锁的文章,无疑要设计一个靠谱的分布式并不太容易,总会出现各种鬼畜的问题;现在就来小述一下,在设计一个分布式锁的过程中,...

小灰灰Blog ⋅ 01/14 ⋅ 0

每日一博 | 基于 redis 的 spring boot 分布式锁实现

每日一博 | 基于 redis 的 spring boot 分布式锁实现 Harries Blog™2018-01-030 阅读 ACESpringAppbeanAPIbuildAOPActionbug 随着现在 分布式 架构越来越盛行,在很多场景下需要使用到 分布...

Harries Blog™ ⋅ 01/03 ⋅ 0

关于redis分布式锁的初步研究与分享

导言: 前段时间项目中用到了分布式锁,所以就对分布式锁进行了一些研究,首先当然是去看redis的分布式锁实现,这里说明一下,分布式锁的实现可以有两种比较简单的方式来实现,一种是redis的...

zhangkay ⋅ 2017/12/16 ⋅ 0

redis分布式锁方案

目录: 1.pipeline 2.跨jvm的id生成器 3.跨jvm的锁实现(watch multi) 4.redis分布式 1. Pipeline 官方的说明是:starts a pipeline,which is a very efficient way to send lots of command......

tony.chen ⋅ 2016/12/26 ⋅ 0

Jedis使用总结

目录: 1.pipeline 2.跨jvm的id生成器 3.跨jvm的锁实现(watch multi) 4.redis分布式 1. Pipeline 官方的说明是:starts a pipeline,which is a very efficient way to send lots of command......

liangyx ⋅ 2013/01/09 ⋅ 2

mysql事务和锁 SELECT FOR UPDATE

事务: 当然有的人用begin /begin work .推荐用START TRANSACTION 是SQL-99标准启动一个事务。 当用set autocommit = 0 的时候,你以后所有的sql都将作为事务处理,直到你用commit确认或 ro...

SibylY ⋅ 2016/08/22 ⋅ 0

Redis实现分布式锁

一、分布式锁是啥? 单机锁的概念:我们正常跑的单机项目(也就是在tomcat下跑一个项目不配置集群)想要在高并发的时候加锁很容易就可以搞定,java提供了很多的机制例如:synchronized、vol...

刺風 ⋅ 2017/10/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

用SQL命令查看Mysql数据库大小

要想知道每个数据库的大小的话,步骤如下: 1、进入information_schema 数据库(存放了其他的数据库的信息) use information_schema; 2、查询所有数据的大小: select concat(round(sum(da...

源哥L ⋅ 42分钟前 ⋅ 0

两个小实验简单介绍@Scope("prototype")

实验一 首先有如下代码(其中@RestController的作用相当于@Controller+@Responsebody,可忽略) @RestController//@Scope("prototype")public class TestController { @RequestMap...

kalnkaya ⋅ 47分钟前 ⋅ 0

php-fpm的pool&php-fpm慢执行日志&open_basedir&php-fpm进程管理

12.21 php-fpm的pool pool是PHP-fpm的资源池,如果多个站点共用一个pool,则可能造成资源池中的资源耗尽,最终访问网站时出现502。 为了解决上述问题,我们可以配置多个pool,不同的站点使用...

影夜Linux ⋅ 56分钟前 ⋅ 0

微服务 WildFly Swarm 管理

Expose Application Metrics and Information 要公开关于我们的微服务的有用信息,我们需要做的就是将监视器模块添加到我们的pom.xml中: 这将使在管理和监视功能得到实现。从监控角度来看,...

woshixin ⋅ 56分钟前 ⋅ 0

java连接 mongo伪集群部署遇到的坑

部署mongo伪集群 #创建mongo数据存放文件地址mkdir -p /usr/local/config1/datamkdir -p /usr/local/config2/data mkdir -p /usr/local/config3/data mkdir -p /usr/local/config1/l......

努力爬坑人 ⋅ 57分钟前 ⋅ 0

React Native & Weex 区别

JS引擎 Weex使用V8, React native使用JSCore JS开发框架 ( Js Framework ) Weex基于vue.js(2W+ star)。小巧轻量的前端开发框架,组件化,数据绑定,2.0引入virtual dom。 ReactNative使用...

东东笔记 ⋅ 今天 ⋅ 1

UIkit 分页组件动态加载简单实现

1. 问题描述 使用过UIkit分页组件的都清楚,UIkit的分页不能动态刷新数据,也就是不能在点击下一页的时候,动态从后台加载数据,并且刷新页数以及该页数上的数据,下面是一个简单实现,没有做...

影狼 ⋅ 今天 ⋅ 0

Mobx入门之三:Provider && inject

上一节中<App/>组件传递状态temperatures给children -- <TemperatureInput />,如果组建是一个tree, 那么属性的传递则会非常繁琐。redux使用Provider给子组件提供store, connect将子组件和s...

pengqinmm ⋅ 今天 ⋅ 0

魔兽世界 7.0版本 S23/S24/S25全职业普通+精锐套

  死亡骑士   (联盟)   (部落)   (精锐)   恶魔猎手   (联盟)   (部落)   (精锐)   德鲁伊   (联盟)   (部落)   (精锐)   猎人   (联盟) ...

wangchen1999 ⋅ 今天 ⋅ 0

maven顶级pom和子pom的版本号批量修改

当一个版本发布,新起一个版本时,我们只需要手动修改一下项目中pom.xml的版本号就可以了。但是如果这个maven项目有很多的子模块项目,那么一个个手动的去改就显得费时费力又繁琐了。还好,m...

ArlenXu ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部