近期使用redis碰到了多个并发处理同一个缓存的情况。在这样的情况下须要进行加锁机制。

本来想使用java自带的ReadWriteLock进行设置读写锁,这也是上家公司使用的方法。

后来经过商讨,给予排除。原因无他,就是java自带的并不能跨jvm进行加锁,意思就是说Aserver上的write锁无法限制Bserver上的同一个方法,也就是说不适用于分布式部署的环境。

后来经过多方面查看资料。终于决定使用redis自身的方法setnx来进行加锁机制。

网上有非常多关于setnx来进行加锁的方法。只是大部分都会有一个同样的缺陷。就是直接使用setnx加锁、使用del释放锁。这样的情况下呢。假设加锁后发生异常导致没有释放锁,则会成为死锁。

參考:http://blog.csdn.net/java2000_wl/article/details/8740911

再后来有一种新的思路,将setnx和expire结合使用,使得锁有一个有效期,这样当发生异常。有效期一过能够自己主动释放锁。这个算是有一些改进。只是因为setnx和expire是两步操作,不具有原子性。假设setnx操作之后发生异常。还是会造成死锁。

然后就考虑有什么办法能够使得setnx和expire操作同步实现并具有原子性。那么转换一下思路,将setnx的value值设置成当前时间过后的某一刻时间(比方1分钟之后),这个是不是就能够间接取代expire操作了。

是的,这样的方式能够实现。

那么如今就是这样的方式加上del基本能够实现加锁和解锁(而且能够解因异常为释放的锁)。

仅仅只是仍有一些缺陷。由于这样的情况会造成竞争关系,參照:https://github.com/huangz1990/redis/commit/18dbaee4f40f435970a09da427b8f45bd26b4072#diff-b643df753e12d0d07a872f91487c957dR34

依据上面链接给出的解决的方法(即新算法思路)使用有效期来推断之后不是删除key,而是直接给key赋予新值,使用getset,然后推断得到的值和原来的值是否相等。相等即获得锁。最后提供了手动解锁的方法(即删除key就能够)。以下给出代码:

  1. public boolean lock(String key, long timeout) {
  2. boolean lockSuccess = false;
  3. ShardedJedis shardedJedis = pool.getResource();
  4. try{
  5. long start = System.currentTimeMillis();
  6. String lockKey = "lock_"+key;
  7. do{
  8. long result = shardedJedis.setnx(lockKey, String.valueOf(System.currentTimeMillis()+LOCKKEY_EXPIRE_TIME*1000+1));
  9. if(result == 1){
  10. lockSuccess = true;
  11. break;
  12. }else{
  13. String lockTimeStr = shardedJedis.get(lockKey);
  14. if(StringUtils.isNumeric(lockTimeStr)){//假设key存在。锁存在
  15. long lockTime = Long.valueOf(lockTimeStr);
  16. if(lockTime < System.currentTimeMillis()){//锁已过期
  17. String originStr = shardedJedis.getSet(lockKey, String.valueOf(System.currentTimeMillis()+LOCKKEY_EXPIRE_TIME*1000+1));
  18. if(StringUtils.isNoneBlank(originStr)&&originStr.equals(lockTimeStr)){//表明锁由该线程获得
  19. lockSuccess = true;
  20. break;
  21. }
  22. }
  23. }
  24. }
  25. //假设不等待,则直接返回
  26. if(timeout == 0){
  27. break;
  28. }
  29. //等待300ms继续加锁
  30. Thread.sleep(300);
  31. }while((System.currentTimeMillis()-start) < timeout);
  32.  
  33. }catch(Exception e){
  34. e.printStackTrace();
  35. }finally{
  36. ...
  37. }
  38. return lockSuccess;
  39. }
  40.  
  41. public void unLock(String key) {
  42. ShardedJedis shardedJedis = pool.getResource();
  43. try{
  44. String lockKey = "lock_"+key;
  45. shardedJedis.del(lockKey);
  46. }catch(Exception e){
  47. e.printStackTrace();
  48. }finally{
  49. ...
  50. }
  51. }

大概就这些吧~~~

共勉!

------补充:

后来在实际应用其中,用代码測试了一下:使用5000并发线程进行加锁操作,会报非常多的connect timeout异常,因为setnx也是在操作redis和redis进行连接,会占用Jedis连接池中的连接,并且没有获得锁的线程一直在等待。所以我们临时弃用。并且redis是单线程的,读写效率都非常高。除非一些特殊业务须要严格的数据同步,否则redis也不须要加锁。

所以等以后碰到这样的特殊业务再具体学习并慎重使用吧。

特意写出来,防止误导别人~~

-------再补充:

事实上redis本身不须要加锁,须要加锁的是进行redis操作之前的数值计算等,这就须要进行锁控制了。而对于这样的是能够考虑使用redis加锁的。仅仅须要给加锁的redis单独起一个实例就可以,初期这样部署会有资源的浪费。中后期能够这样操作。

redis缓存工具Jedis进行跨jvm加锁(分布式应用)--不幸暂弃用--能够做第三方锁使用的更多相关文章

  1. Java 使用Redis缓存工具的图文详细方法

    开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java. (1)Java的安装配置可以参考我们的 Java ...

  2. Windows环境下使用Redis缓存工具的图文详细方法

    一.简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset(有序集合). ...

  3. redis缓存工具类,提供序列化接口

    1.序列化工具类 package com.qicheshetuan.backend.util; import java.io.ByteArrayInputStream; import java.io. ...

  4. redis缓存工具类

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis ...

  5. springboot集成redis缓存

    1.pom.xml增加redis缓存起步依赖(spring-boot-starter-parent包含许多starter版本) <dependency> <groupId>or ...

  6. RedisPool操作Redis,工具类实例

    redis.properties 配置文件内容 redis.pool.maxActive=100redis.pool.maxIdle=20redis.pool.maxWait=3000redis.po ...

  7. redis缓存介绍以及常见问题浅析

    # 没缓存的日子: 对于web来说,是用户量和访问量支持项目技术的更迭和前进.随着服务用户提升.可能会出现一下的一些状况: 页面并发量和访问量并不多,mysql足以支撑自己逻辑业务的发展.那么其实可以 ...

  8. Java SpringBoot使用Redis缓存和Ehcache

    <?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http:// ...

  9. springboot(七).springboot整合jedis实现redis缓存

    我们在使用springboot搭建微服务的时候,在很多时候还是需要redis的高速缓存来缓存一些数据,存储一些高频率访问的数据,如果直接使用redis的话又比较麻烦,在这里,我们使用jedis来实现r ...

随机推荐

  1. activity的横屏和竖屏设置

    主要在清单文件这样配置: <application android:allowBackup="true" android:icon="@drawable/ic_la ...

  2. POJ 3268 Silver Cow Party 正反图最短路

    题目:click here 题意: 给出n个点和m条边,接着是m条边,代表从牛a到牛b需要花费c时间,现在所有牛要到牛x那里去参加聚会,并且所有牛参加聚会后还要回来,给你牛x,除了牛x之外的牛,他们都 ...

  3. 八皇后问题-回溯法(MATLAB)

    原创文章,转载请注明:八皇后问题-回溯法(MATLAB) By Lucio.Yang 1.问题描述 八皇后问题是十九世纪著名数学家高斯于1850年提出的.问题是:在8*8的棋盘上摆放8个皇后,使其不能 ...

  4. idea14 maven项目 jdk编译版本无法修改

    需要在pom中加入以下的内容,即可修改对应的jdk版本 <build> <plugins> <plugin> <groupId>org.apache.m ...

  5. ASP.NET MVC5 学习笔记-3 Model

    1. Model 1.1 添加一个模型 注意,添加属性时可以输入"prop",会自动输入代码段. public class CheckoutAccount { public int ...

  6. selenium 学习笔记 ---新手学习记录(4) 问题总结(java)-autoit3脚本使用

    1.安装autoit3 下载地址:点我下载 (提取码:9633) 提取码 下载完成后,一直下一步即可 2.上传头像使用脚本 代码如下: ControlFocus("打开",&quo ...

  7. 斯坦福 IOS讲义 课件总结 三

    1,@property (nonatomic,readwrite)NSInteger score;注意这里有一个只读和只写的属性,readonly. 2,重写初始化方法也可以改名字和传参数,(改名一般 ...

  8. 利用Crowbar抓取网页异步加载的内容 [Python俱乐部]

    利用Crowbar抓取网页异步加载的内容 [Python俱乐部] 利用Crowbar抓取网页异步加载的内容 在做 Web 信息提取.数据挖掘的过程中,一个关键步骤就是网页源代码的获取.但是出于各种原因 ...

  9. 用elasticsearch索引mongodb数据

    参照网页:单机搭建elasticsearch和mongodb的river 三个步骤: 一,搭建单机replicSet二,安装mongodb-river插件三,创建meta,验证使用 第一步,搭建单机m ...

  10. 什么是Thrift

    起源 百度百科怎么说 thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发. 它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, ...