`
luhantu
  • 浏览: 204054 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

redis实现分布式锁

阅读更多
package com.dev.tool.log.service;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisDataException;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Created by kenny.dong on 2018/3/31.
 */
public class JedisClientTest {
    private final Logger logger = LoggerFactory.getLogger(JedisClientTest.class);
   /**
            * 缓存host
    */
    private String redisHost="xxx";

    /**
     * 缓存端口
     */
    private int  redisPort=6379;

    /**
     * 最大活跃数
     */
    private int maxActive=500;
    /**
     * 最大空闲数
     */
    private int maxIdle=200;
    /**
     * 最大等待时间 毫秒
     */
    private int maxWait=1000;
    /**
     * 获取前是否检测
     */
    private boolean testOnBorrow=true;

    /**
     * 超时时间
     */
    private int timeOut=1000;

    //访问密码
    private String             AUTH           = null;

    private JedisPool jedisPool=null;

    /** 纳秒 */
    private static final long MICRO_SECOND = 1000 * 1000L;

    /**
     * 参考资料:https://blog.csdn.net/jj546630576/article/details/74910343
     * 释放资源,如果是Jedis3.0 以下版本,请注意returnResource用法。
     *参考资料:https://www.cnblogs.com/wangxin37/p/6397783.html
     */
    @Test
    public void distributedLock() throws InterruptedException {
        initJedisPool();
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 4; i++) {//5个任务
            exec.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        String key = "kennyJedisTest";
                        acquireLock(key,3);
                        TimeUnit.MILLISECONDS.sleep(1);
                        releaseLock(key);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        exec.shutdown();  //关闭线程池
        Thread.currentThread().sleep(10000);
    }

    private JedisPool initJedisPool(){
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxActive);
        config.setMaxIdle(maxIdle);
        config.setMaxWaitMillis(maxWait);
        config.setTestOnBorrow(testOnBorrow);
        jedisPool = new JedisPool(config, redisHost, redisPort, timeOut, AUTH);
        return jedisPool;
    }

    /**
     * 获得分布式锁
     * @param key
     * @param timeout
     * @return
     * @throws Exception
     */
    public boolean acquireLock(final String key, final long timeout) throws Exception {
        Assert.notNull(key, "key required");
        Assert.notNull(key, "timeout required");
        try {
            /**
             * 执行过程:
             * 1.通过setnx尝试设置某个key的值,成功(当前没有这个锁)则返回,成功获得锁
             * 2.锁已经存在则获取锁的到期时间,和当前时间比较,超时的话,则设置新的值
             */
            long wait = timeout * 1000;
            while (wait>0) {
                long currentRedisTime = getTime(key);
                long expireTime = timeout * MICRO_SECOND + currentRedisTime + 1;
                logger.info("{}线程 {} begin..., expire:{},redisTime:{}", Thread.currentThread().getName(),key, expireTime,currentRedisTime);
                if (setnx(key, String.valueOf(expireTime)) == 1) {
                    logger.info("{}线程 {} 获得了锁, expire:{}",Thread.currentThread().getName(), key, expireTime);
                    return true;
                }
                String existValue = get(key);//获得已存在的key值的value
                if (!StringUtils.isEmpty(existValue) && NumberUtils.parseNumber(existValue,Long.class) <= currentRedisTime) {//value代表的锁时间超时
                    // 设置现在的锁到期时间,并返回上一个锁到期时间
                    String oldValue = getSet(key, String.valueOf(expireTime));
                    if (NumberUtils.parseNumber(oldValue,Long.class) < currentRedisTime) {
                        logger.info("{}线程 {}获得了锁,oldValue: {}, expire:{},redisTime:{}", Thread.currentThread().getName(),key, oldValue, expireTime,currentRedisTime);
                        return true;
                    }
                }
                logger.info("{}线程 请求{}阻塞中,循环遍历",Thread.currentThread().getName(), key);
                /**
                 * 使用随机时间100 <= x < 150,可以防止饥饿进程的出现,即,当同时到达多个进程,
                 * 只会有一个进程获得锁,其他的都用同样的频率进行尝试,后面有来了一些线程,也以同样的频率申请锁,这将可能导致前面来的锁得不到满足.
                 * 使用随机的等待时间可以一定程度上保证公平性
                 */
                long randomWait = Math.round(Math.random() * (150 - 100)) + 100;
                TimeUnit.MILLISECONDS.sleep(randomWait);
                wait -= randomWait;
            }
        } catch (Throwable e) {
            logger.error("{}线程 acquire lock error : " + e.getMessage(), Thread.currentThread().getName(),e);
            throw new Exception(e);
        }
        return false;
    }

    /**
     * 释放锁
     * @param key
     */
    public void releaseLock(String key) {
        try {
            if (!StringUtils.isEmpty(key) && del(key) == 1) {
                logger.info("{}线程 释放了锁,key is {}",Thread.currentThread().getName(), key);
            }
        } catch (Exception e) {
            logger.error("{}线程 release lock error :" + e.getMessage(),Thread.currentThread().getName(), e);
        }
    }

    /**
     * 在redis取得当前时的方法为执行time命令
           127.0.0.1:6382> time
          1) "1495780564"
          2) "894089"
          第一行为以 UNIX 时间戳格式表示已经过去的秒数
          第二行为当前这一秒已经过去的微秒数
     * @param key
     * @return
     */
    public Long getTime(String key) {
        //boolean broken = false;
        Jedis jedis = null;
        Long currentRedisTime = null;
        try {
            jedis = jedisPool.getResource();
            List<String> redisTime = jedis.time();
            currentRedisTime = NumberUtils.parseNumber(redisTime.get(0), Long.class) * 1000000L +  NumberUtils.parseNumber(redisTime.get(1), Long.class);
        } catch (Exception e) {
            logger.error("redis connect error,key:" + key, e);
            if (jedis != null) {
                 jedis.close();
            }
            //broken = handleJedisException(e);
            throw e;
        } finally {
            if (jedis != null) {
                 jedis.close();
            }
            //closeResource(jedis,broken);
        }
        return currentRedisTime;
    }

    /**
     * 不存在时设置缓存
     * @param key
     * @param value
     * @return 1 if the key was set 0 if the key was not set
     */
    public Long setnx(String key, String value) {
        Jedis jedis=null;
        Long returnLong=null;
        //boolean broken = false;
        try{
            jedis=jedisPool.getResource();
            returnLong= jedis.setnx(key, value);
        }catch (Exception e){
            logger.error("redis_setnx error,key:" + key, e);
            if(jedis!=null){
                jedis.close();
            }
            //broken = handleJedisException(e);
            throw e;
        }finally {
            if(jedis!=null){
                jedis.close();
            }
            //closeResource(jedis,broken);
        }
        return returnLong;
    }

    public String get(String key){
        //boolean broken = false;
        Jedis jedis=null;
        String returnStr=null;
        try{
            jedis=jedisPool.getResource();
            returnStr= jedis.get(key);
        }catch (Exception e){
            logger.error("redis_get error,key:" + key, e);
            if(jedis!=null){
                jedis.close();
            }
           // broken = handleJedisException(e);
            throw e;
        }finally {
            if(jedis!=null){
                jedis.close();
            }
            //closeResource(jedis,broken);
        }
        return returnStr;
    }

    /**
     * set并返回旧值
     * @param key
     * @param value
     * @return
     */
    public String getSet(String key, String value) {
        Jedis jedis=null;
        String returnStr=null;
       // boolean broken = false;
        try{
            jedis=jedisPool.getResource();
            returnStr= jedis.getSet(key, value);
        }catch (Exception e){
            logger.error("redis_getSet error,key:" + key + " value:" + value, e);
            if(jedis!=null){
                jedis.close();
            }
           // broken = handleJedisException(e);
            throw e;
        }finally {
            if(jedis!=null){
                jedis.close();
            }
            //closeResource(jedis,broken);
        }
        return returnStr;
    }

    public Long del(String key){
        Jedis jedis=null;
        Long returnLong=null;
        //boolean broken = false;
        try{
            jedis=jedisPool.getResource();
            returnLong= jedis.del(key);
        }catch (Exception e){
            logger.error("redis_del error,key:" + key, e);
            if(jedis!=null){
               jedis.close();
            }
            //broken = handleJedisException(e);
            throw e;
        }finally {
            if(jedis!=null){
                jedis.close();
            }
            //closeResource(jedis,broken);
        }
        return returnLong;
    }

    /**
     * Handle jedisException, write log and return whether the connection is broken.
     */
    protected boolean handleJedisException(Exception jedisException) {
        if (jedisException instanceof JedisConnectionException) {
            logger.error("Redis connection  lost.", jedisException);
        } else if (jedisException instanceof JedisDataException) {
            if ((jedisException.getMessage() != null) && (jedisException.getMessage().indexOf("READONLY") != -1)) {
                logger.error("Redis connection are read-only slave.", jedisException);
            } else {
                // dataException, isBroken=false
                return false;
            }
        } else {
            logger.error("Jedis exception happen.", jedisException);
        }
        return true;
    }
    /**
     * Return jedis connection to the pool, call different return methods depends on the conectionBroken status.
     */
    protected void closeResource(Jedis jedis, boolean conectionBroken) {
        try {
            if (conectionBroken) {
                jedisPool.returnBrokenResource(jedis);
            } else {
                jedisPool.returnResource(jedis);
            }
        } catch (Exception e) {
            logger.error("return back jedis failed, will fore close the jedis.", e);
            //JedisUtils.destroyJedis(jedis);
        }
    }
}

 

0
1
分享到:
评论

相关推荐

    Java基于redis实现分布式锁代码实例

    Java基于Redis实现分布式锁代码实例 分布式锁的必要性 在多线程环境中,资源竞争是一个常见的问题。例如,在一个简单的用户操作中,一个线程修改用户状态,首先在内存中读取用户状态,然后在内存中进行修改,然后...

    redis实现分布式锁,自旋式加锁,lua原子性解锁

    本文将深入探讨如何使用Redis实现分布式锁,以及如何利用自旋式加锁和Lua脚本实现原子性解锁。 首先,我们来理解分布式锁的基本概念。分布式锁是在多节点之间共享资源时,用于协调各个节点的访问控制机制。在分布式...

    redis实现分布式锁(java/jedis)

    redis实现分布式锁(java/jedis),其中包含工具方法以及使用demo 本资源是利用java的jedis实现 redis实现分布式锁(java/jedis),其中包含工具方法以及使用demo 本资源是利用java的jedis实现

    C++基于redis的分布式锁redisAPI

    本文将深入探讨如何使用C++结合Redis实现分布式锁,并详细讲解Redis API在C++中的应用,以及如何处理与Boost库的集成。 首先,Redis是一个高性能的键值存储数据库,广泛用于缓存、消息队列、分布式锁等场景。分布式...

    Spring Boot Redis 实现分布式锁的方法详解.docx

    Spring Boot 提供了与 Redis 集成的便捷方式,使得开发者能够快速实现分布式锁功能。本篇文章将详细讲解如何利用 Spring Boot 结合 Redis 实现分布式锁。 首先,理解分布式锁的基本概念。分布式锁是在分布式系统中...

    基于Redis的分布式锁的实现方案.pdf

    本文讨论了分布式锁的实现方案,主要基于Redis实现分布式锁,以解决分布式系统中资源访问的同步问题。在分布式系统中,需要协调各个系统或主机之间的资源访问,以避免彼此干扰和保证一致性。分布式锁是控制分布式...

    redis实现分布式锁与java信号量.zip

    在实际项目中,`redis实现分布式锁与java信号量.md`文件可能详细介绍了如何将这些理论知识应用到实际代码中,包括连接Redis、设置和获取锁、释放锁、异常处理等步骤。而`项目说明.zip`文件则可能包含了一个实际的...

    用Redis实现分布式锁_redis_分布式_

    Redis,作为一个高性能的键值存储系统,常被用于实现分布式锁,因为它提供了丰富的数据类型和原子操作。本教程将深入探讨如何利用Redis来构建分布式锁。 一、Redis分布式锁的优势 1. 响应速度:Redis是内存数据库...

    使用Redis实现分布式锁.zip

    本文将深入探讨如何利用Redis实现分布式锁,并对比其与信号量的区别。 一、Redis分布式锁 1. **基本原理**:在Redis中,分布式锁通常通过`SETNX`(Set if Not eXists)命令或者`SET`命令配合`nx`和`ex`选项来创建...

    基于Redis实现分布式锁以及任务队列

    一、Redis实现分布式锁 1. Redis分布式锁的核心是`setnx()`命令,它用于设置键值对,但如果键已存在,则返回失败。在分布式锁的实现中,通常会为锁设置一个超时时间,以防锁意外丢失。例如,使用`Lock:order`作为锁...

    Go-用Redis实现分布式锁与实现任务队列

    本篇文章将深入探讨如何利用Redis实现分布式锁以及如何构建一个基于Redis的任务队列。 分布式锁是解决多节点共享资源时防止数据冲突的关键机制。在Go中,我们通常通过与Redis交互来实现这一功能。Redis提供了`SETNX...

    记录redisson实现redis分布式事务锁

    首先,Redis作为一个内存数据库,其高速读写性能使其成为实现分布式锁的理想选择。分布式锁的主要作用是在多节点环境下保证同一时刻只有一个节点可以执行特定操作,避免并发问题。Redisson的分布式锁通过`RLock`接口...

    Redis实现分布式锁(附源码+讲义)

    分布式锁有很多种解决方案,今天我们要讲的是怎么使用缓存数据库Redis来实现分布式锁。 课程目标:  理解redis分布式锁的应用场景  掌握redis分布式锁的实现原理  掌握redis分布式锁在微服务项目中的应用  掌握...

    基于Redis方式实现分布式锁

    Redis实现分布式锁主要依赖三个基本操作:`SETNX`(Set If Not eXists)、`EXPIRE` 和 `DEL`。 1. **`SETNX` 命令**:只有当键不存在时才设置键值,返回1表示设置成功,返回0表示键已存在,无法设置。 2. **`EXPIRE...

    基于 Redis 的分布式锁

    4. 性能与可用性:使用Redis实现分布式锁,可以保证很高的性能,因为Redis操作非常快速。同时,由于Redis支持主从复制和哨兵模式,提供了高可用性的保障。即便主节点不可用,通过故障转移,其他从节点可以迅速接管,...

    读书笔记:架构实战篇十八Spring Boot Redis实现分布式锁.zip

    读书笔记:架构实战篇十八Spring Boot Redis实现分布式锁

    C#.net Redis分布式锁源码实现

    本篇文章将深入探讨如何在C#.NET环境下利用Redis实现分布式锁,以及相关的核心知识点。 首先,让我们理解什么是分布式锁。分布式锁是在分布式系统中,用于协调不同节点间对共享资源访问的一种工具。它确保在任何...

    基于go和redis实现分布式锁

    本文将深入探讨如何使用Go语言(Golang)和Redis来实现分布式锁,并介绍其中涉及到的关键概念和实现方式,包括看门狗(Watchdog)和红锁(RedLock)策略。 **一、Redis作为分布式锁** Redis因其高性能、轻量级以及...

    springboot基于redis分布式锁

    不过,以上代码提供了一个基本的实现思路,帮助你理解如何在SpringBoot应用中使用Redis实现分布式锁。 通过提供的Word文档,你应该能够找到更详细的配置和使用步骤。记得在实际项目中根据具体需求进行调整,确保...

Global site tag (gtag.js) - Google Analytics