本文探讨在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. 玩一把JS的链式调用

    链式调用我们平常用到很多,比如jQuery中的$(ele).show().find(child).hide(),再比如angularjs中的$http.get(url).success(fn_s).e ...

  2. mysql的压缩特性-需求

    需求:最近有个插入量比较大的应用需要上,每天的插入量在1亿左右,同时会有较少的查询,表的单行长度在0.5k,就数据而言每天有近50G数据,由于每天写一张新表,保留30天的数据,一个月下来也要1.5T, ...

  3. 团队作业4——第一次项目冲刺(Alpha版本)2st day

    一.Daily Scrum Meeting照片 二.燃尽图 三.项目进展 界面 1.四个用户登录界面已经完成. 2.界面内的功能完成了一小部分. 登陆部分 1.QQ授权已经申请,还未通过. 2.通过好 ...

  4. 201521123109《java程序设计》第七周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 ArrayList代码分析 1.1 解释ArrayList的contains源代码 源代码: pub ...

  5. 201521123109《java程序设计》第一周学习总结

    1.本章学习总结 java是我这学期学习的一门新的编程语言,个人觉得java还是很深奥的,对于一门新的计算机语言,就像打开了一扇新世界的大门,有许多东西需要不断学习不断探索,除了上课认真听讲,平时还要 ...

  6. jsp+servlet对于单选按钮和复选框取值并且存放到数据库中

    index.jsp <form action="index.gj?method=toradio" method="post"> <div al ...

  7. Oracle--新建用户以及赋予的权限

    1, 以dba方式登录Oracle 2, 创建用户,Users-->New ... 输入用户名和密码 3, 赋予connect/resource角色 4, 赋予该用户 对视图操作的相关权限 以下 ...

  8. Extjs整合CKEditor富文本编辑器插件

    CKEditor插件官方下载地址: http://ckeditor.com/download/releases 我使用的版本是 ExtJS5.1.0  CKEditor4.4.8 参考文章: http ...

  9. Linux下安全证书申请以及配置到Nginx

    wget https://raw.githubusercontent.com/xdtianyu/scripts/master/lets-encrypt/letsencrypt.shchmod +x l ...

  10. 实验:体会Oracle权限/角色赋予的差异

    环境:Oracle 11.2.0.4 目的:验证业务用户的权限/角色赋予的差异 现在创建两个用户jingyu2和jingyu3: SYS@jyzhao1> create user jingyu2 ...