应用Redis实现分布式锁

主要参考:

  1. http://blog.csdn.net/fjssharpsword/article/details/52250723#
  2. http://www.yiibai.com/redis/strings_getset.html
  3. https://redis.io/commands/expire
  4. https://www.baidu.com/link?url=kuDPE2uEXfjZO3qCggv2OaDGcd9_Mohb_S2Web1VHiY623cg4IJZRwqb0_tGyan7x0BJuV1I46dh-qGWCPEJsa&wd=&eqid=879de37e00008ea500000004583ef0f8
package com.deppon.spring;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import redis.clients.jedis.Jedis;

/**
 *
 * @author ganxj
 *
 */
public class BuildDistributedLockWithRedisDB {
    // 锁名
    private static final String lockName = "lock";

    // 序列名
    private static final String sequenceName = "sequence";

    // 数据失效时间
    private static final int expired = 1;// 1秒超时

    // 当前线程处理当次缓存操作所需时间,(预估值)
    private static final int time = 10 * 1000;

    // setnx --- 当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX不做任何动作,并返回0。
    // getSet -- 将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。
    // 上锁
    public static boolean acquireLock(Jedis jedis, String lock) {
        try {

            // 用于设置成功后的缓存处理时长
            long cacheProcessTime = System.currentTimeMillis() + time;

            // key为空时,争抢锁
            long isSetSuccessWhileKeyIsNil = jedis.setnx(lock, String.valueOf(cacheProcessTime));

            // SETNX成功,则成功获取一个锁,并设置数据失效时间
            if (isSetSuccessWhileKeyIsNil == 1) {
                jedis.expire(lock, expired);
                return true;
            }

            // key不为空,SETNX失败,说明锁被其他客户端保持,检查其是否已经超时
            String lastLockTimeBySomeThread = jedis.get(lock);

            // 如果获取key为空,则重走空key时的加锁流程
            if (lastLockTimeBySomeThread == null) {
                return false;
            }

            // 获取key不为空,则判断是否超时,若未超时,则循环重试
            if (Long.valueOf(lastLockTimeBySomeThread) > System.currentTimeMillis()) {
                return false;
            }

            // 若超时,则进行争抢加锁
            String getOldIfSet = jedis.getSet(lock, String.valueOf(cacheProcessTime));

            // 判断加锁是否成功
            if (getOldIfSet != null && getOldIfSet.equals(lastLockTimeBySomeThread)) {
                return true;
            }

            // 若加锁失败,重头再来
            return false;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    // 释放锁
    public static void releaseLock(Jedis jedis, String lock) {

        try {

            String lastLockTimeBySomeThread = jedis.get(lock);

            if (lastLockTimeBySomeThread == null) {
                return;
            }

            // 避免删除非自己获取得到的锁
            if (System.currentTimeMillis() < Long.valueOf(lastLockTimeBySomeThread)) {
                jedis.del(lock);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @SuppressWarnings("resource")
    public static void main(String[] args) {

        // 任务数
        int produceTaskMaxNumber = 100;

        // 初始化redis中的id序列
        Jedis jedis = new Jedis();
        jedis.set(sequenceName, String.valueOf(Long.valueOf(1)));

        // 初始化线程池
        int corePoolSize = 20;
        int maximumPoolSize=20;
        long keepAliveTime =20;
        TimeUnit unit =TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueu = new ArrayBlockingQueue<Runnable>(30);
        RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueu,
                handler);

        // 创建任务
        for (int i = 1; i <= produceTaskMaxNumber; i++) {
            try {
                threadPool.execute(new BuildDistributedLockWithRedisDB().new RedisLockTestTask());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    class RedisLockTestTask implements Runnable {

        @Override
        public void run() {

            Jedis jedis = new Jedis();
            Boolean lockFlag = true;

            // 循环等待拿锁
            long startTime = System.currentTimeMillis();
            while (lockFlag) {
                if (BuildDistributedLockWithRedisDB.acquireLock(jedis, lockName)) {
                    // 获得锁了,开始执行业务逻辑
                    process(jedis, sequenceName);
                    lockFlag = false;
                }
            }
            long endTime = System.currentTimeMillis();

            System.out.println(Thread.currentThread().getName()
                    + "线程设置的值:"
                    + jedis.get(sequenceName)
                    + "  "
                    + "共消耗时长:" + (endTime - startTime) + "ms");

            // 释放锁
            BuildDistributedLockWithRedisDB.releaseLock(jedis, lockName);

        }

    }

    void process(Jedis jedis,String sequenceName){
        jedis.set(sequenceName, String.valueOf(Long.valueOf(jedis.get(sequenceName)) + 1));
    }

}
 
时间: 2024-10-08 09:47:59

应用Redis实现分布式锁的相关文章

基于Redis的分布式锁到底安全吗(上)?

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多. 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论.由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争

基于Redis实现分布式锁(转载)

原文地址:http://blog.csdn.net/ugg/article/details/41894947 Redis命令介绍使用Redis实现分布式锁,有两个重要函数需要介绍 SETNX命令(SET if Not eXists)语法:SETNX key value功能:当且仅当 key 不存在,将 key 的值设为 value ,并返回1:若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0. GETSET命令语法:GETSET key value功能:将给定 key 的值设为

利用多写Redis实现分布式锁原理与实现分析

在我写这篇文章的时候,其实我还是挺纠结的,因为我这个方案本身也是雕虫小技拿出来显眼肯定会被贻笑大方,但是我最终还是拿出来与大家分享,我本着学习的态度和精神,希望大家能够给与我指导和改进方案. 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂

Redis实现分布式锁

http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的可靠性. 这篇文章的目的就是尝试提出一种官方权威的用Redis实现分布式锁管理器的算法,我们把这个算法称为RedLock,我们相信这个算法会比一般的普通方法更加安全可靠.我们也希望社区能一起分析这个算法,

基于redis的分布式锁

<?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * https://gist.github.com/nickyleach/3694555 */ pc_base::load_sys_class('cache_redis', '', 0); class dist_key_redis { //锁的超时时间 const TIMEOUT = 20; const SL

转载:基于Redis实现分布式锁

转载:基于Redis实现分布式锁  ,出处: http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分

基于Redis实现分布式锁

http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分布式锁,有两个重要函数需要介绍 SETNX命令

基于Redis实现分布式锁-Redisson使用及源码分析

在分布式场景下,有很多种情况都需要实现最终一致性.在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA事务(两阶段提交,数据源可分开),也可以借助消息中间件(消费者处理需要能幂等).通过Observer模式来发布领域事件可以提供很好的高并发性能,并且事件存储也能追溯更小粒度的事件数据,使各个应用系统拥有更好的自治性. 本文主要探讨另外一种实现分布式最终一致性的解决方案--采用分布式锁.基于分布式锁的解决

用redis实现分布式锁

分布式部署中不可避免用到分布式锁,目前比较常用的实现方式一般有基于数据库的乐观锁.基于redis的分布式锁和基于zookeeper的分布式锁.本文只说redis的实现方式,使用jedis作为连接器. 比较简单,直接上代码吧. public class PaasLock { private static final String KEY_NNXX = "NX"; private static final String KEY_EXPX = "PX"; private

Redis实现分布式锁原理与实现分析

一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂等性,那么将会出现重复下单问题. 在接收消息的时候,消息推送重复.如果处理消息的接口无法保证幂等,那么重复消费消息产生的影响可能会非常大. 类似这种场景,我们有很多种方法,可以使用幂等操作,也