本文探讨在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粒度加锁)的更多相关文章

  1. 高并发Redis(Mac)环境配置(一)

    一.产生原因: SNS交互型网站的兴起,对于高并发,大负载数据的操作,海量数据的存储和访问 NoSql四种类型: 键值存储(Redis优点可以快速查询,缺点缺少存储的结构化)              ...

  2. 高并发redis分布式锁

    1.方法一 2方法二

  3. redis处理高并发

    参考: https://www.cnblogs.com/wanlei/p/10464517.html 关于Redis处理高并发 Redis的高并发和快速原因 1.Redis是基于内存的,内存的读写速度 ...

  4. redis高可用,保证高并发

    目录 redis如何通过读写分离来承载读请求QPS超过10万+ redis replication以及master持久化对主从架构的安全意义 redis主从复制原理.断点续传.无磁盘化复制.过期key ...

  5. 面试连环炮系列(一):如何保证Redis高可用和高并发

    如何保证Redis高可用和高并发? Redis主从架构,一主多从,可以满足高可用和高并发.出现实例宕机自动进行主备切换,配置读写分离缓解Master读写压力. Redis高可用方案具体怎么实施? 使用 ...

  6. Redis的高并发、持久化、高可用架构设计

    就是如果你用redis缓存技术的话,肯定要考虑如何用redis来加多台机器,保证redis是高并发的,还有就是如何让Redis保证自己不是挂掉以后就直接死掉了,redis高可用 我这里会选用我之前讲解 ...

  7. 高并发架构系列:Redis并发竞争key的解决方案详解

    https://blog.csdn.net/ChenRui_yz/article/details/85096418 https://blog.csdn.net/ChenRui_yz/article/l ...

  8. Redis:解决分布式高并发修改同一个Key的问题

    本篇文章是通过watch(监控)+mutil(事务)实现应用于在分布式高并发处理等相关场景.下边先通过redis-cli.exe来测试多个线程修改时,遇到问题及解决问题. 高并发下修改同一个key遇到 ...

  9. 关于Redis处理高并发

    Redis的高并发和快速原因 1.Redis是基于内存的,内存的读写速度非常快: 2.Redis是单线程的,省去了很多上下文切换线程的时间: 3.Redis使用多路复用技术,可以处理并发的连接.非阻塞 ...

随机推荐

  1. 团队作业8——第二次项目冲刺(Beta阶段)5.27

    1.当天站立式会议照片 会议内容: 本次会议为第七次会议 本次会议在陆大楼2楼召开,本次会议内容: ①:检查总结上次任务完成情况 ②:安排今天的分工 ③:对昨天的问题进行讨论 2. 每个人的工作 (有 ...

  2. 【Alpha阶段】第五次scrum meeting

    一.会议照片 二.会议内容 姓名 学号 负责模块 昨日任务完成度 今日任务 杨爱清 099 界面设计和交互功能 完成 去酷狗选择合适的轻音乐 杨立鑫 100 数据库搭建和其他 完成 继续对数据库进行编 ...

  3. Linux下安装oracle jdk

    从官网下载对应的 .tar.gz压缩文件. 在linux某个目录下解压 到根目录下的etc文件夹下编辑profile文件,在文件的末尾加上 JAVA_HOME=/usr/local/jdk1.8.0_ ...

  4. 201521123066《Java程序设计》第八周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 1.List中指定元素的删除(题目4-1) 1.1 实验总结** 用cont ...

  5. Java课程设计——GUI密码生成器201521123035

    1.团队课程设计博客链接 (http://www.cnblogs.com/wuling15/p/7061857.html) 2.个人负责模块或任务说明 (1)确定课题并进行任务分工 (2)编写随机数产 ...

  6. 201521123056 《Java程序设计》第12周学习总结

    1. 本周学习总结 2. 书面作业 将Student对象(属性:int id, String name,int age,double grade)写入文件student.data.从文件读出显示. 1 ...

  7. 今天的第一个程序-南阳acm输入三个数排序

    #include<stdio.h>main(){    int a,b,c,t;    scanf("%d%d%d",&a,&b,&c);    ...

  8. Core Java 简单谈谈HashSet

    同学们在看这个问题的时候,我先提出者两个问题,然后大家带着问题看这个文章会理解的更好. HashSet为什么添加元素时不能添加重复元素? HashSet是否添加null元素? 打开源码, 我们看到如下 ...

  9. jvm系列:Java GC 分析

    Java GC就是JVM记录仪,书画了JVM各个分区的表演. 什么是 Java GC Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之 ...

  10. Linux第一篇【介绍、安装Ubuntu、基本目录结构】

    Linux介绍 Linux:不管是不是我们这些学编程的都肯定会听说过这么一个系统,一般地,我们在PC端都是用Windows操作系统,那我们学习Linux操作系统有什么用呢??? 由于我们的JAVAEE ...