原文: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. (一)问候 HtmlUnit

    第一节: HtmlUnit 简介 htmlunit 是一款开源的java 页面分析工具,读取页面后,可以有效的使用htmlunit分析页面上的内容.项目可以模拟浏览器运行,被誉为java浏览器的开源实 ...

  2. SQL Case when 的使用方法 (转)

    Case具有两种格式.简单Case函数和Case搜索函数. --简单Case函数 CASE sex WHEN '1' THEN '男' WHEN '2' THEN '女' ELSE '其他' END ...

  3. Docker容器跨主机通信之:直接路由方式

    一.Docker网络基本原理 直观上看,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包:此外,如果不同子网之间要进行通信,需要额外的路由机制. Docker ...

  4. 动态规划面试题基础合集1--数学三角形,LIS , LCS, CSD

    动态规划的一般思路是分为四步,即:寻找最优子结构.递归定义最优子结构.自底向上求解最优子结构和构造最优解. 接下来我列举出几个常见的动态规划面试题进行说明. (1)数学三角形:比较简单,直接贴一个我看 ...

  5. python 比timedelta强大的多的 relativedelta

    datetime包中的timedelta功能有限,比如,一个月的delta都没法表示.dateutil包中的relativedelta要强大很多. 年月日周的delta都能支持,还有weekday, ...

  6. DDD领域模型企业级系统(一)

    领域模型的基本构造块: 1.实体(Entity):有业务生命周期,使用标识进行跟踪. 2.值对象(Value Object):无业务生命周期,用来描述实体. 3.服务(Service):无状态的行为类 ...

  7. Sublime Text安装SVN插件

    下载插件 Sublime Text2/3 SVN插件 点击下载 安装插件 点击设置(Preferences)->浏览程序包(Browse Packages,,,),新建TortoiseSVN文件 ...

  8. Gitlab Webhooks, External Services, and API(一)

    一. 和外部服务进行集成 Gitlab支持和不同的外部服务进行集成,比如可以和聊天工具,Slack或者Campfire进行集成,或者和项目管理工具进行集成.如Assembla或者Pivotal Tra ...

  9. mysql分布式集群实现原理

    做MySQL集群,例如:利用mysql cluster ,mysql proxy,mysql replication,drdb等等 有人会问mysql集群,根分表有什么关系吗?虽然它不是实际意义上的分 ...

  10. ACM训练计划建议(转)

    ACM训练计划建议 From:freecode#  Date:2015/5/20 前言: 老师要我们整理一份训练计划给下一届的学弟学妹们,整理出来了,费了不少笔墨,就也将它放到博客园上供大家参考. 菜 ...