Redis实现分布式锁

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介:

/**
 * @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
     * @see com.fx.platform.components.lock.IBillLockHandler#tryLock(com.fx.platform.components.lock.IBillIdentify)
     * @param billIdentify
     * @return
     */
    public boolean tryLock(IBillIdentify billIdentify) {
        return tryLock(billIdentify, 0L, null);
    }

    /**
     * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false
     * @author http://blog.csdn.net/java2000_wl
     * @see com.fx.platform.components.lock.IBillLockHandler#tryLock(com.fx.platform.components.lock.IBillIdentify,
     *      long, java.util.concurrent.TimeUnit)
     * @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
     * @see com.fx.platform.components.lock.IBillLockHandler#lock(com.fx.platform.components.lock.IBillIdentify)
     * @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
     * @see com.fx.platform.components.lock.IBillLockHandler#unLock(com.fx.platform.components.lock.IBillIdentify)
     * @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
     * @see com.fx.platform.components.lock.IBatchBillLockHandler#tryLock(java.util.List)
     * @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
     * @see com.fx.platform.components.lock.IBatchBillLockHandler#unLock(java.util.List)
     * @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/happyday56/p/3454086.html,如需转载请自行联系原作者
相关文章
|
弹性计算 网络安全
通过云企业网实现不同账号、相同地域下的ECS实例内网互通
阿里云的专有网络类型实例,如果在同一VPC下,不同安全组互相授权即可内网互通,不同VPC默认是互相隔离的,还需要通过云企业网打通VPC才能实现内网互通本例为不同账号,不同VPC下的实例内网互通(图为实验开始前测试内网不通)第一步:安全组授权安全组互相授权操作方法:注意1:需要两台服务器的安全组都添加.
5927 0
通过云企业网实现不同账号、相同地域下的ECS实例内网互通
|
10月前
|
芯片
浮动CPU和定点CPU的主要区别是什么
浮动CPU和定点CPU的主要区别在于处理数据的方式不同。浮动CPU支持浮点运算,能高效处理小数和高精度计算;而定点CPU仅支持整数运算,适用于对精度要求不高的场景。
|
人工智能 编解码
|
Kubernetes 安全 前端开发
三分钟了解RBAC模型
时至今日,RBAC访问控制模型已经渗入IT领域的多个方面,有传统技术方面的操作系统、数据库、中间件Web服务器,有新兴技术方面的Kubernetes、Puppet、OpenStack等。RBAC访问控制模型能得到如此丰富而广泛的使用,得益于它基于用户与角色关系分配权限进行访问控制的核心理念。
三分钟了解RBAC模型
|
C# iOS开发 Java
****Objective-C 中的方法的调用
oc语言中采用特定的语言调用类或者实例(对象)的方法称为发送消息或者方法调用。 oc中方法的调用有两种:  第一种: [类名或对象名 方法名];   [ClassOrInstance method]; [ClassOrInstance method:arg1]; ...
1260 0
|
Web App开发 应用服务中间件 Apache
Get/POST方法提交的长度限制
 1.    Get方法长度限制 Http Get方法提交的数据大小长度并没有限制,HTTP协议规范没有对URL长度进行限制。这个限制是特定的浏览器及服务器对它的限制。 如:IE对URL长度的限制是2083字节(2K+35)。 下面就是对各种浏览器和服务器的最大处理能力做一些说明. Microsoft Internet Explorer (Browser) IE浏览器对URL的最大限制
11127 2
|
存储 消息中间件 缓存
【Cassandra从入门到放弃系列 一】概述及基本架构
【Cassandra从入门到放弃系列 一】概述及基本架构
4143 1
|
存储 弹性计算 移动开发
阿里云无影云桌面使用教程(新手5分钟入门)
阿里云无影云桌面怎么使用?无影云桌面怎么购买?阿里云无影云桌面用户名和密码在哪查询?无影云桌面怎么连接互联网?
13122 3
阿里云无影云桌面使用教程(新手5分钟入门)
|
机器学习/深度学习 前端开发 TensorFlow
深度学习:Tensorflow的基本概念和张量
深度学习:Tensorflow的基本概念和张量
244 0
|
运维 监控 安全
最右App:架构演进之路
本次阿里云云栖社区行业圆桌论坛上,最右App技术总监许林、阿里云技术专家张翔贺、高级技术专家闵庆欢共同探讨了最右App的上云实践之路,并且分享了最右App在移动加速和个性化推荐以及安全方面实战经验。对话行业大咖,引领云端科技,畅谈云上话题,尽在阿里云云栖社区行业圆桌论坛。
13691 0

热门文章

最新文章