文档章节

redis 设置分布式锁

chaun
 chaun
发布于 2016/11/22 20:47
字数 1561
阅读 42
收藏 3
点赞 0
评论 0

 

一、pom.xml

<dependency>
		<groupId>redis.clients</groupId>
		<artifactId>jedis</artifactId>
		<version>2.8.0</version>
		<type>jar</type>
	</dependency>

<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>slf4j-log4j12</artifactId>
		<version>1.7.5</version>
	</dependency>
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>slf4j-api</artifactId>
		<version>1.7.5</version>
	</dependency>

二、java文件

package com.commnon;

import java.util.ResourceBundle;

import org.apache.commons.lang3.StringUtils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisPoolManager {
	private volatile static JedisPoolManager manager;
	private final JedisPool pool;

	private JedisPoolManager() {

		try {
			// 加载redis配置
			ResourceBundle bundle = ResourceBundle.getBundle("redis");
			if (bundle == null) {
				throw new IllegalArgumentException("[redis.properties] is not found!");
			}
			// 创建jedis池配置实例
			JedisPoolConfig config = new JedisPoolConfig();
			// 设置池配置项值
			String maxTotal = bundle.getString("redis.pool.maxTotal");
			config.setMaxTotal(Integer.parseInt(maxTotal));

			String maxIdle = bundle.getString("redis.pool.maxIdle"); 
			config.setMaxIdle(Integer.parseInt(maxIdle));

			String minIdle = bundle.getString("redis.pool.minIdle");  
			config.setMinIdle(Integer.parseInt(minIdle));

			String maxWaitMillis = bundle.getString("redis.pool.maxWaitMillis");  
			config.setMaxWaitMillis(Long.parseLong(maxWaitMillis));

			String testOnBorrow = bundle.getString("redis.pool.testOnBorrow"); 
			config.setTestOnBorrow("true".equals(testOnBorrow));

			String testOnReturn = bundle.getString("redis.pool.testOnReturn"); 
			config.setTestOnReturn("true".equals(testOnReturn));

			String server = bundle.getString("redis.server"); 
			
			if (StringUtils.isEmpty(server)) {
				throw new IllegalArgumentException("JedisPool redis.server is empty!");
			}

			String[] host_arr = server.split(",");
			if (host_arr.length > 1) {
				throw new IllegalArgumentException(
						"JedisPool redis.server length > 1");
			}

			String[] arr = host_arr[0].split(":");

			// 根据配置实例化jedis池
			System.out.println("***********init JedisPool***********");
			System.out.println("host->" + arr[0] + ",port->" + arr[1]);
			pool = new JedisPool(config, arr[0], Integer.parseInt(arr[1]));
		} catch (Exception e) {
			throw new IllegalArgumentException("init JedisPool error", e);
		}

	}

	public static JedisPoolManager getMgr() {
		if (manager == null) {
			synchronized (JedisPoolManager.class) {
				if (manager == null) {
					manager = new JedisPoolManager();
				}
			}
		}
		return manager;
	}

	public Jedis getResource() {
		return pool.getResource();
	}

	public void destroy() {
		pool.destroy();
	}

	public void close() {
		pool.close();
	}
}
package com.lock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;

import com.commnon.JedisPoolManager;

public class RedisLockUtil {

	private final static Logger LOG = LoggerFactory.getLogger(RedisLockUtil.class);
	
	public static boolean acquireLock(String lock, long expired) {
	    // 1. 通过SETNX试图获取一个lock
	    boolean success = false;
	    JedisPoolManager jedisPoolManager = JedisPoolManager.getMgr();
	    Jedis jedis = jedisPoolManager.getResource();
	    
	    long value = System.currentTimeMillis() + expired + 1;  //锁超时时间  
	    
	    LOG.info(value+"");    
	    
	    long acquired = jedis.setnx(lock, String.valueOf(value));
	    //SETNX成功,则成功获取一个锁
	    if (acquired == 1)      
	        success = true;
	    //SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时
	    else {
	        long oldValue = Long.valueOf(jedis.get(lock));
	        //超时
	        if (oldValue < System.currentTimeMillis()) {
                // 假设多个线程(非单jvm)同时走到这里  
	            String getValue = jedis.getSet(lock, String.valueOf(value));               
	            // 获取锁成功
                // 但是走到这里时每个线程拿到的oldValue肯定不可能一样(因为getset是原子性的)  
                // 假如拿到的oldValue依然是expired的,那么就说明拿到锁了
	            if (Long.valueOf(getValue) == oldValue) 
	                success = true;
	            // 已被其他进程捷足先登了
	            else 
	                success = false;
	        }
	        //未超时,则直接返回失败
	        else             
	            success = false;
	   }        
	   return success;      
	}
	 
	//释放锁
	public static void releaseLock(String lock) {
		JedisPoolManager jedisPoolManager = JedisPoolManager.getMgr();
	    Jedis jedis = jedisPoolManager.getResource();     
	    long current = System.currentTimeMillis();       
	    // 避免删除非自己获取得到的锁
	    if (current < Long.valueOf(jedis.get(lock)))
	        jedis.del(lock);      
	}
}

三、redis.properties

# Redis server ip and port
redis.server=192.168.1.111:6379

# Redis pool
redis.pool.maxTotal=20
redis.pool.maxIdle=10
redis.pool.minIdle=1
redis.pool.maxWaitMillis=60000
redis.pool.testOnBorrow=true
redis.pool.testOnReturn=true

使用redisTemplate

public Long acquireLock(final String lockName,final long expire){ 	
	    return redisTemplate.execute(new RedisCallback<Long>() {     
              public Long doInRedis(RedisConnection connection) {            
 	        byte[] lockBytes = redisTemplate.getStringSerializer().serialize(lockName); 
            	boolean locked = connection.setNX(lockBytes, lockBytes); 
            	connection.expire(lockBytes, expire);
 		   if(locked){ 
		      return 1L;
 		}       
       	            return 0L; 
                }
   		}); 
  	}
//原子操作
public String getAndSet(final String key,final String value){ 
		return  redisTemplate.execute(new RedisCallback<String>() {
 			@Override 
			public String doInRedis(RedisConnection connection) 
					throws DataAccessException { 
				byte[] result = connection.getSet(redisTemplate.getStringSerializer().serialize(key),
 				redisTemplate.getStringSerializer().serialize(value));
 				if(result!=null){ 					
                                    return new String(result); 	
			 } 
				return null; 		
	                } 	
    	    }); 
	}

 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis distributed lock implementation.
 *
 * @author zhengcanrui
 */
public class RedisLock {

    private static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    private RedisTemplate redisTemplate;

    private static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 100;

    /**
     * Lock key path.
     */
    private String lockKey;

    /**
     * 锁超时时间,防止线程在入锁以后,无限的执行等待
     */
    private int expireMsecs = 60 * 1000;

    /**
     * 锁等待时间,防止线程饥饿
     */
    private int timeoutMsecs = 10 * 1000;

    private volatile boolean locked = false;

    /**
     * Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000 msecs.
     *
     * @param lockKey lock key (ex. account:1, ...)
     */
    public RedisLock(RedisTemplate redisTemplate, String lockKey) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey + "_lock";
    }

    /**
     * Detailed constructor with default lock expiration of 60000 msecs.
     *
     */
    public RedisLock(RedisTemplate redisTemplate, String lockKey, int timeoutMsecs) {
        this(redisTemplate, lockKey);
        this.timeoutMsecs = timeoutMsecs;
    }

    /**
     * Detailed constructor.
     *
     */
    public RedisLock(RedisTemplate redisTemplate, String lockKey, int timeoutMsecs, int expireMsecs) {
        this(redisTemplate, lockKey, timeoutMsecs);
        this.expireMsecs = expireMsecs;
    }

    /**
     * @return lock key
     */
    public String getLockKey() {
        return lockKey;
    }

    private String get(final String key) {
        Object obj = null;
        try {
            obj = redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    byte[] data = connection.get(serializer.serialize(key));
                    connection.close();
                    if (data == null) {
                        return null;
                    }
                    return serializer.deserialize(data);
                }
            });
        } catch (Exception e) {
            logger.error("get redis error, key : {}", key);
        }
        return obj != null ? obj.toString() : null;
    }

    private boolean setNX(final String key, final String value) {
        Object obj = null;
        try {
            obj = redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    Boolean success = connection.setNX(serializer.serialize(key), serializer.serialize(value));
                    connection.close();
                    return success;
                }
            });
        } catch (Exception e) {
            logger.error("setNX redis error, key : {}", key);
        }
        return obj != null ? (Boolean) obj : false;
    }

    private String getSet(final String key, final String value) {
        Object obj = null;
        try {
            obj = redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    byte[] ret = connection.getSet(serializer.serialize(key), serializer.serialize(value));
                    connection.close();
                    return serializer.deserialize(ret);
                }
            });
        } catch (Exception e) {
            logger.error("setNX redis error, key : {}", key);
        }
        return obj != null ? (String) obj : null;
    }

    /**
     * 获得 lock.
     * 实现思路: 主要是使用了redis 的setnx命令,缓存了锁.
     * reids缓存的key是锁的key,所有的共享, value是锁的到期时间(注意:这里把过期时间放在value了,没有时间上设置其超时时间)
     * 执行过程:
     * 1.通过setnx尝试设置某个key的值,成功(当前没有这个锁)则返回,成功获得锁
     * 2.锁已经存在则获取锁的到期时间,和当前时间比较,超时的话,则设置新的值
     *
     * @return true if lock is acquired, false acquire timeouted
     * @throws InterruptedException in case of thread interruption
     */
    public synchronized boolean lock() throws InterruptedException {
        int timeout = timeoutMsecs;
        while (timeout >= 0) {
            long expires = System.currentTimeMillis() + expireMsecs + 1;
            String expiresStr = String.valueOf(expires); //锁到期时间
            if (this.setNX(lockKey, expiresStr)) {
                // lock acquired
                locked = true;
                return true;
            }

            String currentValueStr = this.get(lockKey); //redis里的时间
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                //判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
                // lock is expired

                String oldValueStr = this.getSet(lockKey, expiresStr);
                //获取上一个锁到期时间,并设置现在的锁到期时间,
                //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    //防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受

                    //[分布式的情况下]:如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
                    // lock acquired
                    locked = true;
                    return true;
                }
            }
            timeout -= DEFAULT_ACQUIRY_RESOLUTION_MILLIS;

            /*
                延迟100 毫秒,  这里使用随机时间可能会好一点,可以防止饥饿进程的出现,即,当同时到达多个进程,
                只会有一个进程获得锁,其他的都用同样的频率进行尝试,后面有来了一些进行,也以同样的频率申请锁,这将可能导致前面来的锁得不到满足.
                使用随机的等待时间可以一定程度上保证公平性
             */
            Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);

        }
        return false;
    }


    /**
     * Acqurired lock release.
     */
    public synchronized void unlock() {
        if (locked) {
            redisTemplate.delete(lockKey);
            locked = false;
        }
    }

}

 

参考

http://www.blogjava.net/hello-yun/archive/2014/01/15/408988.html

http://www.58maisui.com/2016/10/11/708/ 

http://www.cnblogs.com/0201zcr/p/5942748.html

http://www.cnblogs.com/PurpleDream/p/5559352.html

http://blog.csdn.net/ugg/article/details/41894947

 

© 著作权归作者所有

共有 人打赏支持
chaun
粉丝 85
博文 262
码字总数 96237
作品 0
深圳
高级程序员
Spring-data-redis + redis 分布式锁(一)

分布式锁的解决方式 基于数据库表做乐观锁,用于分布式锁。(适用于小并发) 使用memcached的add()方法,用于分布式锁。 使用memcached的cas()方法,用于分布式锁。(不常用) 使用redis的setnx...

xiaolyuh ⋅ 2017/11/15 ⋅ 0

如何在springcloud分布式系统中实现分布式锁?

转载请标明出处: http://blog.csdn.net/forezp/article/details/68957681 本文出自方志朋的博客 最近在看分布式锁的资料,看了 Josial L的《Redis in Action》的分布式锁的章节。实现思路是...

forezp ⋅ 2017/04/03 ⋅ 0

Spring-data-redis + redis 分布式锁(二)

分布式锁的解决方式 基于数据库表做乐观锁,用于分布式锁。(适用于小并发) 使用memcached的add()方法,用于分布式锁。 使用memcached的cas()方法,用于分布式锁。(不常用) 使用redis的setnx...

xiaolyuh ⋅ 2017/11/16 ⋅ 0

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

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

Harries Blog™ ⋅ 01/03 ⋅ 0

Redis与Zookeeper实现分布式锁的区别

简介 一般而言,大多数系统实现分布式锁服务都会优先使用Redis;但阅读Zookeeper时可知,Zookeeper的一个很重要应用方向就是分布式锁。那么两者实现分布式锁服务的区别是什么呢。 实现难度 ...

沈渊 ⋅ 2017/10/15 ⋅ 0

分布式锁原理及常用实现

起因 前段时间,看到redis作者发布的一篇文章《Is Redlock safe?》,Redlock是redis作者基于redis设计的分布式锁的算法。文章起因是有一位分布式的专家写了一篇文章《How to do distributed...

偶尔诗文 ⋅ 2016/11/09 ⋅ 0

jedisLock—redis分布式锁实现

搬运工:http://www.cnblogs.com/0201zcr/p/5942748.html jedisLock—redis分布式锁实现 一、使用分布式锁要满足的几个条件: 系统是一个分布式系统(关键是分布式,单机的可以使用Reentrant...

北极之北 ⋅ 2017/01/06 ⋅ 2

使用Redis实现分布式锁及其优化

使用Redis实现分布式锁及其优化 Mz的博客2017-11-0128 阅读 RedisJava分布式 目前实现分布式锁的方式主要有数据库、Redis和Zookeeper三种,本文主要阐述利用Redis的相关命令来实现分布式锁。...

Mz的博客 ⋅ 2017/11/01 ⋅ 0

分布式锁总结

0 前言 可以先看下之前写的实现分布式锁的方案 分布式锁的实现 然后再来看下下面的总结。 1 设置锁超时时间 redis、数据库等实现的分布式锁,需要设置锁超时时间的原因在于:其他客户端无法得...

乒乓狂魔 ⋅ 2016/11/09 ⋅ 0

基于redis的分布式锁的分析与实践

前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了。乐观锁和悲观锁最根本的区别...

菜蚜 ⋅ 01/12 ⋅ 2

没有更多内容

加载失败,请刷新页面

加载更多

下一页

AppDelegate 设置Root相关

self.window = UIWindow.init(frame: UIScreen.main.bounds) self.window?.backgroundColor = UIColor.white self.window?.makeKeyAndVisible() self.window?.rootViewController = RootTabB......

west_zll ⋅ 19分钟前 ⋅ 0

Java并发系列5--倒计时器CountDownLatch

今天讲一个倒计时器工具,叫CountDownLatch。需要这个工具的场景大概有:当所有的小任务都完成之后,再启动大任务。 先看代码: public class CountDownLatchDemo {static final CountDow...

大大枣 ⋅ 20分钟前 ⋅ 0

SpreadJS使用进阶指南 - 使用 NPM 管理你的项目

前言 SpreadJS作为一款性能出众的纯前端电子表格控件,自2015年发布以来,已经被广泛应用于各领域“在线Excel”数据管理项目中。NPM,作为管理Node.js库最有力的手段,解决了很多NodeJS代码部...

葡萄城控件技术团队 ⋅ 21分钟前 ⋅ 0

Mac下IntelliJ IDEA快捷键大全

https://blog.csdn.net/lisongjia123/article/details/54949364

细节探索者 ⋅ 24分钟前 ⋅ 0

建造者模式

1、工厂模式中创建的对象大都是简单的对象 复杂的产品类并且拥有不同的属性特点的管理就需要用到建造者模式 2、建造者模式: 将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以...

职业搬砖20年 ⋅ 25分钟前 ⋅ 0

Mysql数据库开发 怎么优化SQL语句?

 1) 现场抓出慢查询语句 show full processlist;   2) 配置参数:   slow_query_log_file = ON 慢查询开启开关   long_query_time =2 记录大于2秒的sql语句   log_queries_not_usi...

老男孩Linux培训 ⋅ 25分钟前 ⋅ 0

Laravel 安装执行php artisan migrate 出现字段过长错误

最近在自己研究Laravel Laravel版本:5.6 PHP版本:7.1.9 Mysql版本:5.7.19 Apache版本:2.4.27 系统版本:windows10 首先要保证电脑安装了composer,和node.js 执行命令 composer global ...

Marhal ⋅ 31分钟前 ⋅ 0

ELK6.0日志从收集到处理完整版教程(二)

ELK简介 Elasticsearch 开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。也可以认为ElasticSearch是一...

bz_z ⋅ 34分钟前 ⋅ 0

Spark项目之电商用户行为分析大数据平台之(七)数据调研--基本数据结构介绍

目录 一、user_visit_action(Hive表) 1.1 表的结构 1.2 表的说明 二、user_info(Hive表) 2.1 表的结构 2.2 表的说明 三、task(MySQL表) 3.1 表的结构 3.2 表的说明 四、工作流程...

xiaomin0322 ⋅ 38分钟前 ⋅ 0

评分卡模型剖析之一(woe、IV、ROC、信息熵)

信用评分卡模型在国外是一种成熟的预测方法,尤其在信用风险评估以及金融风险控制领域更是得到了比较广泛的使用,其原理是将模型变量WOE编码方式离散化之后运用logistic回归模型进行的一种二...

火力全開 ⋅ 39分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部