springboot高并发redis细粒度加锁(key粒度加锁)
本文探讨在web开发中如何解决并发访问带来的数据同步问题。
1、需求:
通过REST接口请求并发访问redis,例如:将key=fusor:${order_id} 中的值+1;
2、场景:
设想,多线程对key=fusor:${order_id}并发访问触发了竞态条件,例如两个线程同时发现key=fusor:${order_id}的值为5,然后并且+1回写6,这个时候就出现了问题,最终的值为6而不是7。
3、粗粒度锁:
这时候,普遍的做法是加锁,但是如果对整个访问redis的动作加锁,那么等于多个线程串行访问了!
4、细粒度加锁:
我们这里的做法是对key进行细粒度加锁,每个key拥有一把锁,只对key进行并发控制,key与key之间允许并发。
直接上代码
package com.xiaoju.dqa.fusor.utils; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import java.util.Random;
import java.util.UUID; @Component
public class SexyLockUtil<K> {
private static Logger logger = LoggerFactory.getLogger(SexyLockUtil.class); @Autowired
private SexyLocker<K> sexyLocker; public KeyLocker<K> getKeyLocker(K key) {
return new KeyLocker<K>(sexyLocker, key);
} public static class KeyLocker<K> {
private UUID uuid = UUID.randomUUID();
private SexyLocker sexyLocker;
private K key; public KeyLocker(SexyLocker sexyLocker, K key) {
this.sexyLocker = sexyLocker;
this.key = key;
} public boolean lockWithRetry(int expireTime, int retryTimes) {
Random random = new Random();
for (int i = 0; i < retryTimes; i++) {
if (this.lock()) {
return true;
} else {
try {
Thread.sleep(random.nextInt(50));
} catch (InterruptedException e) { }
}
}
return false;
} public boolean lock() {
try {
this.sexyLocker.lock(key);
return true;
} catch (Exception e) {
return false;
} } public boolean unlock() {
try {
this.sexyLocker.unlock(key);
return true;
} catch (Exception e) {
return false;
}
}
}
}
package com.xiaoju.dqa.fusor.utils; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Semaphore; @Component
public class SexyLocker<K> {
private static Logger logger = LoggerFactory.getLogger(SexyLockUtil.class); private final ConcurrentMap<K, Semaphore> map = new ConcurrentHashMap<K, Semaphore>();
private final ThreadLocal<Map<K, LockInfo>> local = new ThreadLocal<Map<K, LockInfo>>() {
@Override
protected Map<K, LockInfo> initialValue() {
return new HashMap<K, LockInfo>();
}
}; public void lock(K key) {
if (key == null)
return;
LockInfo info = local.get().get(key);
if (info == null) {
Semaphore current = new Semaphore(1);
current.acquireUninterruptibly();
Semaphore previous = map.put(key, current);
if (previous != null)
previous.acquireUninterruptibly();
local.get().put(key, new LockInfo(current));
} else {
info.lockCount++;
}
} public void unlock(K key) {
if (key == null)
return;
LockInfo info = local.get().get(key);
if (info != null && --info.lockCount == 0) {
info.current.release();
map.remove(key, info.current);
local.get().remove(key);
}
} public void lock(K[] keys) {
if (keys == null)
return;
for (K key : keys) {
lock(key);
}
} public void unlock(K[] keys) {
if (keys == null)
return;
for (K key : keys) {
unlock(key);
}
} private static class LockInfo {
private final Semaphore current;
private int lockCount; private LockInfo(Semaphore current) {
this.current = current;
this.lockCount = 1;
}
}
}
springboot高并发redis细粒度加锁(key粒度加锁)的更多相关文章
- 高并发Redis(Mac)环境配置(一)
一.产生原因: SNS交互型网站的兴起,对于高并发,大负载数据的操作,海量数据的存储和访问 NoSql四种类型: 键值存储(Redis优点可以快速查询,缺点缺少存储的结构化) ...
- 高并发redis分布式锁
1.方法一 2方法二
- redis处理高并发
参考: https://www.cnblogs.com/wanlei/p/10464517.html 关于Redis处理高并发 Redis的高并发和快速原因 1.Redis是基于内存的,内存的读写速度 ...
- redis高可用,保证高并发
目录 redis如何通过读写分离来承载读请求QPS超过10万+ redis replication以及master持久化对主从架构的安全意义 redis主从复制原理.断点续传.无磁盘化复制.过期key ...
- 面试连环炮系列(一):如何保证Redis高可用和高并发
如何保证Redis高可用和高并发? Redis主从架构,一主多从,可以满足高可用和高并发.出现实例宕机自动进行主备切换,配置读写分离缓解Master读写压力. Redis高可用方案具体怎么实施? 使用 ...
- Redis的高并发、持久化、高可用架构设计
就是如果你用redis缓存技术的话,肯定要考虑如何用redis来加多台机器,保证redis是高并发的,还有就是如何让Redis保证自己不是挂掉以后就直接死掉了,redis高可用 我这里会选用我之前讲解 ...
- 高并发架构系列:Redis并发竞争key的解决方案详解
https://blog.csdn.net/ChenRui_yz/article/details/85096418 https://blog.csdn.net/ChenRui_yz/article/l ...
- Redis:解决分布式高并发修改同一个Key的问题
本篇文章是通过watch(监控)+mutil(事务)实现应用于在分布式高并发处理等相关场景.下边先通过redis-cli.exe来测试多个线程修改时,遇到问题及解决问题. 高并发下修改同一个key遇到 ...
- 关于Redis处理高并发
Redis的高并发和快速原因 1.Redis是基于内存的,内存的读写速度非常快: 2.Redis是单线程的,省去了很多上下文切换线程的时间: 3.Redis使用多路复用技术,可以处理并发的连接.非阻塞 ...
随机推荐
- (三)、LNMP的搭建,并制作rpm包
中小型规模网站集群架构:yum仓库搭建 : 矮哥linux运维群:93324526 编译的三条命令的规则 ./configure 就是在本地创建了一个Makefile文件 (也就是指定一下各种配置参数 ...
- MySQLzip archive版本(5.7.19)安装教程
1. 从官网下载zip archive版本http://dev.mysql.com/downloads/mysql/ 2. 解压缩至相应目录,并配置环境变量(将*\bin添加进path中): 3. ...
- 201521123057 《Java程序设计》第1周学习总结
本章学习总结 第一章是本学期Java学习的起步,主要介绍了Java版本,平台以及JDK,JRE,JDK的内容 书面作业 为什么java程序可以跨平台运行?执行java程序的步骤是什么?(请用自己的语言 ...
- Sublime使用Ctrl+`作为快捷键弹出Console没有反映的解决办法
很多Sublime新人都遇到了这个问题,到网上搜,信息很片面,而且不少都是旧版本的.于是有了这篇文章. 默认Sublime使用Ctrl+`作为快捷键弹出Console,但不同的系统抑或安装 ...
- 201521123072《java程序设计》第十四周学习总结
201521123072<java程序设计>第十四周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库 ...
- 手写Maven的archetype项目脚手架
一.背景 maven是一个很好的代码构建工具,采用“约定优先于配置”的原则进行项目管理,相信很多的java开发者应该都了解maven并可能在工作当中都是通过maven来管理项目的,在创建的项目的时候, ...
- 多线程:head first Thread.join()
不使用Thread.join() 测试线程 先上代码: /** * Created by Zero on 2017/8/23. */ public class TestJoin implements ...
- Eclipse rap 富客户端开发总结(12) :Rap 优化之组件的销毁
一.概述 经过几个月的rap 项目实战,总结了一些小经验,在这里总结一下,希望对大家有所帮助. 二.销毁的处理 相信学习rap 的同学都知道,swt 中提供了许多的组件,像lab ...
- DAU新解
写在回归之时 --------------------------- 抱歉,已经超过三年没有回到这里了. 浮躁,虚荣,一颗心没有落到地上,也没有好好总结一下自己.怀念最开始那个时候的状态,安静的做点事 ...
- JavaScript总体的介绍【JavaScript介绍、定义函数方式、对象类型、变量类型】
什么是JavaScript? 我们可以从几个方面去说JavaScript是什么: 基于对象 javaScript中内置了许多对象供我们使用[String.Date.Array]等等 javaScrip ...