目录

1.项目场景:

2.问题代码 :

3.修改后的代码:

4.实战示例:

5.更新:setIfAbsent

6.再次更新:redisson


1.项目场景:

锁主要是用来实现资源共享同步,只有获取到了锁才能访问该同步代码,否则等待其他线程使用结束释放锁。

2.问题代码 :

@Autowired
private RedisTemplate redisTemplate;/*** 加锁*/
public boolean getLock(String key) {try {long count = redisTemplate.opsForValue().increment(key, 1);//此段代码出现异常则会出现死锁问题,key一直都存在if(count == 1){//设置有效期2秒redisTemplate.expire(key, 2, TimeUnit.SECONDS);return true;}//如果存在表示重复return false;} catch (Exception e) {logger.error("redis加锁异常", e);redisTemplate.delete(key);        //出现异常删除锁return true;}
}

当正常情况没有问题,但是当计数器设置成功后,服务出现异常,那么这个key会永远存在,这样肯定不行。

Redis Incr 命令将 key 中储存的数字值增一,如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作,且将key的有效时间设置为长期有效。

3.修改后的代码:

@Autowired
private RedisTemplate redisTemplate;/*** 加锁*/
public boolean getLock(String key) {try {long count = redisTemplate.opsForValue().increment(key, 1);if(count == 1){//设置有效期2秒redisTemplate.expire(key, 2, TimeUnit.SECONDS);return true;}else{long time = redisTemplate.getExpire(key,TimeUnit.SECONDS);if(time == -1){//设置失败重新设置过期时间redisTemplate.expire(key, 2, TimeUnit.SECONDS);return true;}}//如果存在表示重复return false;} catch (Exception e) {logger.error("redis加锁异常", e);redisTemplate.delete(key);      //出现异常删除锁return true;}
}

4.实战示例:

场景:同一个接口,必须等待上个线程处理完后,业务才能处理。

Service层:RedisLock.java

package com.test.utils;import java.util.concurrent.TimeUnit;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import lombok.extern.slf4j.Slf4j;/*** Redis锁*/
@Service
@Slf4j
public class RedisLock {@Autowiredprivate RedisTemplate<String, ?> redisTemplate;/*** 加锁* @param key    key* @param timeExpire 过期时间单位秒* @return   true表示key存在*/public boolean getLock(String key, long timeExpire) {try {long count = redisTemplate.opsForValue().increment(key, 1);if(count == 1){//设置有效期timeExpireredisTemplate.expire(key, timeExpire, TimeUnit.SECONDS);return false;}else{long time = redisTemplate.getExpire(key,TimeUnit.SECONDS);if(time == -1){//设置失败重新设置过期时间redisTemplate.expire(key, timeExpire, TimeUnit.SECONDS);return false;}}//如果存在表示重复return true;} catch (Exception e) {log.error("redis加锁异常", e);redisTemplate.delete(key);    //出现异常删除锁return false;}}/*** 解锁* @param key    key* @return   true表示成功*/public boolean unLock(String key) {try {redisTemplate.delete(key);    //删除keyreturn true;} catch (Exception e) {log.error("redis解锁异常", e);redisTemplate.delete(key);    //出现异常删除锁return false;}}}

Controller层:TestController.java

package com.test.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import com.test.utils.RedisLock;import lombok.extern.slf4j.Slf4j;@RestController
@Slf4j
public class TestController{@Autowiredprivate RedisLock redisLock;/*** 测试并发*/@RequestMapping(value = "/test", method = RequestMethod.GET)public String test(String key) {try {long expireTime = 30000;   //有效时长(毫秒)long beginTime = System.currentTimeMillis(); //开始时间//判断同一时间是否有多个用户操作boolean flagLock = redisLock.getLock(key, 6);if(flagLock){//如果锁存在,尝试重新获取锁while (redisLock.getLock(key, 6)) {//执行时长(毫秒)long time = System.currentTimeMillis() - beginTime;//防止死循环if(time > expireTime){log.error("超时,请稍后重试");return "超时,请稍后重试!";}Thread.sleep(100);}}//TODO 业务操作...return "success";} catch (Exception e) {log.error("出错:", e);return "出错,请联系管理员!";}finally {//解锁redisLock.unLock(key);}}}

5.更新:setIfAbsent

将redis版本升级到2.1以上,可以直接使用setIfAbsent设置过期时间,使用 4 个参数的重载方法是原子性的。

redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);

说明:当key不存在,将key的值设为value,并设置过期时间,返回true;若给定的key已经存在,则不做任何动作,并返回false。此方法是原子性的。

使用setIfAbsent

@Autowired
private RedisTemplate redisTemplate;/*** 加锁* @param key key* @param value value* @param timeout 过期时间单位秒* @param true表示key不存在*/
public boolean getLock(String key, String value, long timeout) {try {//原子性操作boolean flag = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);//如果存在false表示重复return flag;} catch (Exception e) {logger.error("redis加锁异常", e);redisTemplate.delete(key);      //出现异常删除锁return true;}
}

6.再次更新:redisson

推荐使用Redisson来实现分布式锁,当然前面两种方式也是可行的,条条大路通罗马。

什么是redisson:

Redisson - 是一个高级的分布式协调Redis客服端,Redisson API 侧重于分布式开发,充分的利用了Redis键值数据库提供的一系列优势,在Java实用工具包中常用接口的基础上,为使用者提供了一系列具有分布式特性的常用工具类。

引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.7.5</version>
</dependency>

application.properties

# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接密码(默认为空)
spring.redis.password=
# Redis服务器连接端口
spring.redis.port=6379

Redisson的配置类

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.io.IOException;@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private String port;@Value("${spring.redis.password}")private String password;/*** RedissonClient,单机模式* @return* @throws IOException*/@Beanpublic RedissonClient redissonClient() {Config config = new Config();config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);return Redisson.create(config);}
}

Redisson分布式锁业务类


import java.util.concurrent.TimeUnit;import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import lombok.extern.slf4j.Slf4j;@Slf4j
@Service
public class TestService {@AutowiredRedissonClient redissonClient;private final static String LOCK_KEY = "TEST_KEY";int count = 50;public void seckill() {//定义锁RLock rlock = redissonClient.getLock(LOCK_KEY);try {//尝试加锁,最大等待时间30秒,上锁10秒自动解锁boolean flag = rlock.tryLock(30, 10, TimeUnit.SECONDS);if (flag) {log.info("线程:" + Thread.currentThread().getName() + "获得了锁");log.info("剩余数量:{}", --count);}} catch (Exception e) {log.error("程序执行异常:", e);//释放锁rlock.unlock();} finally {log.info("线程:" + Thread.currentThread().getName() + "准备释放锁");//释放锁rlock.unlock();}}
}

源码地址包含三种方式:

https://download.csdn.net/download/u011974797/86772996

使用redis incr处理并发,存在死锁问题相关推荐

  1. java操作redis并发_使用Redis incr解决并发问题的操作

    项目背景: 1.新增问题件工单,工单中有工单编码字段,工单编码字段的规则为 "WT"+yyyyMMdd+0000001. 2.每天的工单生成量是30W,所以会存在并发问题 解决思路 ...

  2. 使用redis incr处理并发问题

    一.背景 最近公司某个短信接口因为没有加验证码限制被恶意调用,最好的解决办法是做在发送短信请求前做一个验证码验证通过后再调用短信接口.但是由于需要立马改动,借此使用了一下 "缓兵之计&quo ...

  3. Redis incr解决并发问题

    项目背景: 1.新增问题件工单,工单中有工单编码字段,工单编码字段的规则为 "WT"+yyyyMMdd+0000001. 2.每天的工单生成量是30W,所以会存在并发问题 解决思路 ...

  4. redis 的incr 高并发 原子性计数器

    前言:6月底 公司录单的人比较多,由于先前的系统用的同步锁 ,我们是多服务实例,导致出现重复单号的问题,我想到的解决办法有两种 ,第一种是 Redis锁 第二种是自增key,下面实现的是用第二种方法 ...

  5. php redis incr过期时间,Redis 利用 incr 和 expire 来限流, 并发导致过期时间失效问题...

    当某一个接口需要限流时,可以采用redis的incr来递增,记录访问次数, 以及 expire 来设置失效时间. 大概的代码如下: r = redis.Redis.connect() key = &q ...

  6. Redis适用于高并发的递增、递减功能

    递增指令:incr(默认从0开始) 递减指令:decr(默认从0开始,递减会出现负数,这点跟memcache不一样,mc到0) 如下: 附上shardedJedisPool和JedisCluster的 ...

  7. 29 | 无锁的原子操作:Redis如何应对并发访问?

    文章目录 Redis核心技术与实战 实践篇 29 | 无锁的原子操作:Redis如何应对并发访问? 并发访问中需要对什么进行控制? Redis 的两种原子操作方法 Redis核心技术与实战 实践篇 2 ...

  8. redis incr和incrBy的使用

     最近用incr和incrBy在接口里做了下埋点统计每天请求总数,这两个命令还是挺好用的,先说下这俩命令吧 注:redis后台服务是串行的单线程执行,不存在并发,即多线程调用Incr/incrby方法 ...

  9. Redis如何应对并发访问

    Redis如何应对并发访问 Redis如果在业务中运用那么肯定需要考虑并发问题,如多个用户对同一个商品进行扣减,这时并发执行很可能导致商品数量不对,那么Redis如何来避免这些问题呢?一般分为两种解决 ...

最新文章

  1. 独家 | 一文读懂概率论学习:贝叶斯理论(附链接)
  2. 细胞膜内流体应力分析
  3. dockerfile 的使用
  4. 整数转罗马数字Python解法
  5. mit数据集_DriveSeg:动态驾驶场景分割数据集
  6. java引擎组件_Java 脚本引擎入门
  7. MiniO对象存储服务 磁盘缓存快速入门 ​​​​​​​
  8. 安装pypcap = 安装flex:the fast lexical analyser + 安装libpcap-1.7.4
  9. python数字组合算法_python猜数字游戏快速求解解决方案
  10. jsp+aJax 登陆成功
  11. 帆软高级函数应用之文本函数
  12. java hex to ascii_使用java实现hex和ascii码的转换
  13. ExpandableListView点击Group动态获取Child数据源
  14. 基于微信小程序的学生健康管理系统
  15. 计算机基础知识及键盘熟悉实验报告,微型计算机组成和键盘操作 实验报告
  16. Pr 水墨动画转场效果
  17. 前端高级进阶13本经典书籍
  18. 瑞利 随机 matlab,matlab产生瑞利分布随机信号
  19. webapp期末作业-oneapp
  20. 这款吊打Chrome、Edge的浏览器,时隔573天再度更新

热门文章

  1. 2009奥巴马的秋季开学演讲稿
  2. TCP协议和TCP连接
  3. Anchor Base 和 Anchor Free
  4. 在计算机上怎么搜共享打印机,如何共享电脑中的打印机?共享电脑打印机步骤...
  5. 初看SOA:SOA是什么?
  6. 什么是Platform SDK?
  7. 使用Godaddy的API批量修改域名的NameServers,指向CloudFlare的NS,享受免费的抗DDOS保护!
  8. 如何用cmd命令进入mysql
  9. 关于海思3559A板子(润和出厂)启动的阶段性总结
  10. C语言 队列(循环队列和链队初始化进出队等基本操作)