原文:https://blog.csdn.net/qq1010267837/article/details/79697572

依赖jar包

compile group: 'redis.clients', name: 'jedis', version:'2.8.1'
compile group: 'org.springframework.data', name: 'spring-data-redis', version:'1.6.5.RELEASE'

/**
* Redis的分布式锁对象
* Created by zhengjy on 2017/3/6.
*/
public interface RedisLock extends AutoCloseable { /**
* 释放分布式锁
*/
void unlock();
}
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.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import redis.clients.jedis.Jedis; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit; /**
* @Resource(name="stringRedisTemplate")
* private RedisAtomicClient redisAtomicClient;
*
* 提供Redis一些不直接支持的原子性的操作,很多实现采用了lua脚本
* Created by zhengjy on 2017/3/6.
*/
public class RedisAtomicClient {
private static final Logger logger = LoggerFactory.getLogger(RedisAtomicClient.class); private final RedisTemplate redisTemplate;
private final StringRedisTemplate stringRedisTemplate; private static final String INCR_BY_WITH_TIMEOUT = "local v;" +
" v = redis.call('incrBy',KEYS[1],ARGV[1]);" +
"if tonumber(v) == 1 then\n" +
" redis.call('expire',KEYS[1],ARGV[2])\n" +
"end\n" +
"return v";
private static final String COMPARE_AND_DELETE =
"if redis.call('get',KEYS[1]) == ARGV[1]\n" +
"then\n" +
" return redis.call('del',KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end"; public RedisAtomicClient(RedisTemplate redisTemplate){
this.redisTemplate = redisTemplate;
this.stringRedisTemplate = new StringRedisTemplate();
this.stringRedisTemplate.setConnectionFactory(redisTemplate.getConnectionFactory());
this.stringRedisTemplate.afterPropertiesSet();
} /**
* 根据key获得对应的long类型数值,不存在则返回null(本方法使用string序列化方式)
* @param key
* @return
*/
public Long getLong(String key){
try {
String val = stringRedisTemplate.opsForValue().get(key); if(val == null){
return null;
}else{
return Long.valueOf(val);
}
} catch(Exception e){
logger.error("get key error:"+key, e);
return null;
}
} /**
* 计数器,支持设置失效时间,如果key不存在,则调用此方法后计数器为1(本方法使用string序列化方式)
* @param key
* @param delta 可以为负数
* @param timeout 缓存失效时间
* @param timeUnit 缓存失效时间的单位
* @return
*/
public Long incrBy(String key, long delta, long timeout, TimeUnit timeUnit){
List<String> keys = new ArrayList<>();
keys.add(key);
long timeoutSeconds = TimeUnit.SECONDS.convert(timeout, timeUnit);
String[] args = new String[2];
args[0] = String.valueOf(delta);
args[1] = String.valueOf(timeoutSeconds);
Object currentVal = stringRedisTemplate.execute(new DefaultRedisScript<>(INCR_BY_WITH_TIMEOUT, String.class), keys, args); if(currentVal instanceof Long){
return (Long)currentVal;
}
return Long.valueOf((String)currentVal);
} /**
* 获取redis的分布式锁,内部实现使用了redis的setnx。只会尝试一次,如果锁定失败返回null,如果锁定成功则返回RedisLock对象,调用方需要调用RedisLock.unlock()方法来释放锁.
* <br/>使用方法:
* <pre>
* RedisLock lock = redisAtomicClient.getLock(key, 2);
* if(lock != null){
* try {
* //lock succeed, do something
* }finally {
* lock.unlock();
* }
* }
* </pre>
* 由于RedisLock实现了AutoCloseable,所以可以使用更简介的使用方法:
* <pre>
* try(RedisLock lock = redisAtomicClient.getLock(key, 2)) {
* if (lock != null) {
* //lock succeed, do something
* }
* }
* </pre>
* @param key 要锁定的key
* @param expireSeconds key的失效时间
* @return 获得的锁对象(如果为null表示获取锁失败),后续可以调用该对象的unlock方法来释放锁.
*/
public RedisLock getLock(final String key, long expireSeconds){
return getLock(key, expireSeconds, 0, 0);
} /**
* 获取redis的分布式锁,内部实现使用了redis的setnx。如果锁定失败返回null,如果锁定成功则返回RedisLock对象,调用方需要调用RedisLock.unlock()方法来释放锁
* <br/>
* <span style="color:red;">此方法在获取失败时会自动重试指定的次数,由于多次等待会阻塞当前线程,请尽量避免使用此方法</span>
*
* @param key 要锁定的key
* @param expireSeconds key的失效时间
* @param maxRetryTimes 最大重试次数,如果获取锁失败,会自动尝试重新获取锁;
* @param retryIntervalTimeMillis 每次重试之前sleep等待的毫秒数
* @return 获得的锁对象(如果为null表示获取锁失败),后续可以调用该对象的unlock方法来释放锁.
*/
public RedisLock getLock(final String key, final long expireSeconds, int maxRetryTimes, long retryIntervalTimeMillis){
final String value = key.hashCode()+""; int maxTimes = maxRetryTimes + 1;
for(int i = 0;i < maxTimes; i++) {
String status = stringRedisTemplate.execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
Jedis jedis = (Jedis) connection.getNativeConnection();
String status = jedis.set(key, value, "nx", "ex", expireSeconds);
return status;
}
});
if ("OK".equals(status)) {//抢到锁
return new RedisLockInner(stringRedisTemplate, key, value);
} if(retryIntervalTimeMillis > 0) {
try {
Thread.sleep(retryIntervalTimeMillis);
} catch (InterruptedException e) {
break;
}
}
if(Thread.currentThread().isInterrupted()){
break;
}
} return null;
} private class RedisLockInner implements RedisLock{
private StringRedisTemplate stringRedisTemplate;
private String key;
private String expectedValue; protected RedisLockInner(StringRedisTemplate stringRedisTemplate, String key, String expectedValue){
this.stringRedisTemplate = stringRedisTemplate;
this.key = key;
this.expectedValue = expectedValue;
} /**
* 释放redis分布式锁
*/
@Override
public void unlock(){
List<String> keys = Collections.singletonList(key);
stringRedisTemplate.execute(new DefaultRedisScript<>(COMPARE_AND_DELETE, String.class), keys, expectedValue);
} @Override
public void close() throws Exception {
this.unlock();
}
}
}

扩展redisTemplate实现分布式锁的更多相关文章

  1. Redis实现分布式锁2

    redisTemplate实现分布式锁 /** * 分布式锁-加锁 * @param key * @param value 当前时间+超时时间 System.currentTimeMillis()+t ...

  2. MasaFramework -- 锁与分布式锁

    前言 什么是锁?什么是分布式锁?它们之间有什么样的关系? 什么是锁 加锁(lock)是2018年公布的计算机科学技术名词,是指将控制变量置位,控制共享资源不能被其他线程访问.通过加锁,可以确保在同一时 ...

  3. redis分布式锁Redisson扩展

    如果大家项目中Redis是多机部署的可以来好好看看这篇实现,讲的非常好. 使用Redisson实现分布式锁,Spring AOP简化之   源码 Redisson概述 Redisson是一个在Redi ...

  4. Redisson分布式锁的简单使用

    一:前言 我在实际环境中遇到了这样一种问题,分布式生成id的问题!因为业务逻辑的问题,我有个生成id的方法,是根据业务标识+id当做唯一的值! 而uuid是递增生成的,从1开始一直递增,那么在同一台机 ...

  5. spring boot redis分布式锁

    随着现在分布式架构越来越盛行,在很多场景下需要使用到分布式锁.分布式锁的实现有很多种,比如基于数据库. zookeeper 等,本文主要介绍使用 Redis 做分布式锁的方式,并封装成spring b ...

  6. spring boot redis分布式锁 (转)

    一. Redis 分布式锁的实现以及存在的问题 锁是针对某个资源,保证其访问的互斥性,在实际使用当中,这个资源一般是一个字符串.使用 Redis 实现锁,主要是将资源放到 Redis 当中,利用其原子 ...

  7. 使用Redis分布式锁处理并发,解决超卖问题

    一.使用Apache ab模拟并发压测 1.压测工具介绍 $ ab -n 100 -c 100 http://www.baidu.com/ -n表示发出100个请求,-c模拟100个并发,相当是100 ...

  8. Redis分布式锁—SETNX+Lua脚本实现篇

    前言 平时的工作中,由于生产环境中的项目是需要部署在多台服务器中的,所以经常会面临解决分布式场景下数据一致性的问题,那么就需要引入分布式锁来解决这一问题. 针对分布式锁的实现,目前比较常用的就如下几种 ...

  9. 【Redis 分布式锁】(1)一把简单的“锁”

    原文链接:https://www.changxuan.top/?p=1230 在单体架构向分布式集群架构演进的过程中,项目中必不可少的一个功能组件就是分布式锁.在开发团队有技术积累的情况下,做为团队的 ...

随机推荐

  1. Linux下./configure && make && make install 编译安装和卸载

    正常的编译安装/卸载: 源码的安装一般由3个步骤组成:配置(configure).编译(make).安装(make install).   configure文件是一个可执行的脚本文件,它有很多选项, ...

  2. Mysql建表语句

    create table messages( id int unsigned primary key auto_increment, sender varchar(64) not null, gett ...

  3. Sqlserver双机热备文档(无域)

    1. 配制环境 OS:Win7    DB:SQL Server R2 2. 基本配制 1.      开启sqlServer服务如下图-1 图-1 2.      开启sqlServer的tcp/i ...

  4. **PHP删除数组中特定元素的两种方法array_splice()和unset()

    方法一: 复制代码代码如下: <?php$arr1 = array(1,3, 5,7,8);$key = array_search(3, $arr1); if ($key !== false)  ...

  5. mysql索引(btree索引和hash索引的区别)

    所有MySQL列类型可以被索引.根据存储引擎定义每个表的最大索引数和最大索引长度.所有存储引擎支持每个表至少16个索引,总索引长度至少为256字节.大多数存储引擎有更高的限制. 索引的存储类型目前只有 ...

  6. CCF CSP 201512-3 画图

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201512-3 画图 问题描述 用 ASCII 字符来画图是一件有趣的事情,并形成了一门被称为 ...

  7. linux 101 hacks 6history 及相关关键字

    如果你经常使用命令行,那么有效的使用命令历史机制将会使效率获得极大提升.事实上,一旦你掌握了我在下面给出的 15 个例子, 你就会发现使用命令行将更有乐趣 是吗?我学一学呢 技巧 50:使用 HIST ...

  8. mongodb导入导出

    导出 mongoexport -d 数据库 -c 表名 -o 输出文件名 例:mongoexport -d Mongodb_DataManager -c Kujiale_Users -o Kujial ...

  9. ava包(package)的命名规范,java中package命名规则

    Java的包名都有小写单词组成,类名首字母大写:包的路径符合所开发的 系统模块的 定义,比如生产对生产,物资对物资,基础类对基础类.以便看了包名就明白是哪个模块,从而直接到对应包里找相应的实现. 由于 ...

  10. 灵活的按键处理程序 FlexibleButton

    前言 正好工作中用到按键处理,需要处理单击.长按等按键事件,然后就造了这么一个轮子,为了以后更方便地加入其它的项目中使用,遂将其开源到 GitHub 中. 后面发现 RT-Thread 软件包里也有一 ...