引子: 解决缓存击穿问题

synchronized (this){代码块}

public synchronized Map<String,List<Catelog2Vo>> getCatalog]sonFromDb()

:SpringBoot所有的组件在容器中都是单例的。

优点:速度快

所以这样只能有一个人拿到锁进入逻辑, 但是也是在单体服务下可行,在分布条件下,几个服务就生成几把锁(应该问题不大)

测试1: 有3个人进入拿到了锁

原因:  拿到锁的那个线程确认缓存没有,去查数据库,查询后释放锁 ,刚释放还没来得及往缓存中存数据, 另一个线程就拿到锁再进行操作

解决方案:  把 (往缓存中存数据) 放到锁中    [稍微影响效率]

这里推荐下阿里的jetcache 全部都封装好了

redis分布式锁

分布式中,即使多个服务,也只让一个人去查查数据

原理: redis  set hello world NX

先看缓冲中有没有, 没有的话就占分布式锁,执行下面的逻辑

可能遇到问题

1.拿到锁之后,执行完逻辑,在释放锁之前突然断电,无法释放锁导致其他服务无法获取,造成死锁  (解决办法: 锁设置过期时间:30S)

还有问题: 若是刚加锁,还没来得及设置过期时间,就闪断

解决办法:  原子操作

Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111",30, TimeUnit.SECONDS);

删锁的时候遇到问题

1.假设业务时间过长,锁自动过期了  就会有其他线程进来

2.自己的锁过期了,删掉了其他人正在用的锁

解决办法:可以加V值改为UUID,删锁执行判断UUID进行删除

String uuid = UUID.randomUUID().toString();

    新问题(无原子性): 因为数据传输慢的原因,去redis判断uuid是否是自己,判断后,在然后往回传结果true的路上  ,K过期了,其他人占了锁 , 此时,删除的锁,也是别人的锁

    解决办法: 删锁的原子性,利用lua脚本 

                String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1]\n" +
"then\n" +
" return redis.call('del',KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
// 删除锁
redisTemplate.execute(new DefaultRedisScript<Long>(luaScript, Long.class), Arrays.asList("LOCK_KEY_CATALOG_JSON"), uuid);
Arrays.asList("LOCK_KEY_CATALOG_JSON") ==  uid  就删除锁

加锁保证原子性,解锁保证原子性

问题: 锁的自动续期问题,  [把时间放长一点]

Redisson(单机redis)

导入依赖

        <dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.3</version>
</dependency>

配置

@Configuration
public class MyRedissonConfig { /**
* 注入客户端实例对象
*/
@Bean(destroyMethod="shutdown")
public RedissonClient redisson(@Value("${spring.redis.host}") String host, @Value("${spring.redis.port}")String port) throws IOException {
// 1.创建配置
Config config = new Config();
config.useSingleServer().setAddress("redis://" + host + ":" + port);// 单节点模式
// config.useSingleServer().setAddress("rediss://" + host + ":" + port);// 使用安全连接
// config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");// 集群模式
// 2.创建redisson客户端实例
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}

测试

RedissonClient redissonClient

实现分布式锁

锁的自动续期+30S (看门狗 : 10秒续一次)   ,一旦设置过期时间,看门狗会失效

可重入锁(抢占一把锁)

    public Map<String, List<Catalog2VO>> getCatalogJsonFromDBWithRedissonLock() {
// 1.抢占分布式锁,同时设置过期时间
RLock lock = redisson.getLock("LOCK_KEY_CATALOG_JSON");
lock.lock(30, TimeUnit.SECONDS);
try {
// 2.查询DB
Map<String, List<Catalog2VO>> result = getCatalogJsonFromDB();
return result;
} finally {
// 3.释放锁
lock.unlock();
}
}
RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock();
// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS); // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}

公平锁(先到先得)

//所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,
也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。 RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock(); // 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS); // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
...
fairLock.unlock();

读写锁(写锁控制读锁)

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();
// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS); // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock(); //抽象lock(代表读写锁)

闭锁

等待全部成才能加锁

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(5); //等5个走完才结束
latch.await(); // 在其他线程或其他JVM里()
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();

信号量

做限流

提前在redis中存入信号量{"semaphore":"10"}

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.acquire();
//获取信号量-1,获取不到就一直等待
semaphore.tryAcquire(23, TimeUnit.SECONDS);  /.尝试等待23秒,

//其他线程 semaphore.release(); //释放信号量+1

可过期性信号量

红锁(大多数锁上就行)

注意锁的粒度

办法:  要么加锁牺牲效率,    要么允许暂时的不一致,有最终一致性就行     丨(加了缓存过期时间)

问题: 脏数据问题,  2号慢  , 3号读到了脏数据,也更新了脏数据     丨 (加了缓存过期时间)

经常修改的数据,直接读数据库就行

本地锁 & 分布式锁的更多相关文章

  1. 图解Janusgraph系列-并发安全:锁机制(本地锁+分布式锁)分析

    图解Janusgraph系列-并发安全:锁机制(本地锁+分布式锁)分析 大家好,我是洋仔,JanusGraph图解系列文章,实时更新~ 图数据库文章总目录: 整理所有图相关文章,请移步(超链):图数据 ...

  2. 本地锁、redis分布式锁、zk分布式锁

    本地锁.redis分布式锁.zk分布式锁 https://www.cnblogs.com/yjq-code/p/dotnetlock.html 为什么要用锁? 大型站点在高并发的情况下,为了保持数据最 ...

  3. SpringBoot--防止重复提交(锁机制---本地锁、分布式锁)

    防止重复提交,主要是使用锁的形式来处理,如果是单机部署,可以使用本地缓存锁(Guava)即可,如果是分布式部署,则需要使用分布式锁(可以使用zk分布式锁或者redis分布式锁),本文的分布式锁以red ...

  4. .net下 本地锁、redis分布式锁、zk分布式锁的实现

    为什么要用锁? 大型站点在高并发的情况下,为了保持数据最终一致性就需要用到技术方案来支持.比如:分布式锁.分布式事务.有时候我们在为了保证某一个方法每次只能被一个调用者使用的时候,这时候我们也可以锁来 ...

  5. 分布式改造剧集2---DIY分布式锁

    前言: ​ 好了,终于又开始播放分布式改造剧集了.前面一集中(http://www.cnblogs.com/Kidezyq/p/8748961.html)我们DIY了一个Hessian转发实现,最后我 ...

  6. Redisson分布式锁实现

    转: Redisson分布式锁实现 2018年09月07日 15:30:32 校长我错了 阅读数:3303   转:分布式锁和Redisson实现 概述 分布式系统有一个著名的理论CAP,指在一个分布 ...

  7. 【转】Redis学习笔记(四)如何用Redis实现分布式锁(1)—— 单机版

    原文地址:http://bridgeforyou.cn/2018/09/01/Redis-Dsitributed-Lock-1/ 为什么要使用分布式锁 这个问题,可以分为两个问题来回答: 为什么要使用 ...

  8. 分布式锁和Redisson实现

    http://thoreauz.com/2017/08/20/language/java/%E5%9F%BA%E7%A1%80/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81 ...

  9. redission 分布式锁

    https://my.oschina.net/haogrgr/blog/469439   分布式锁和Redisson实现 Aug 20, 2017 CONTENTS 概述 分布式锁特性 Redis实现 ...

  10. [转]分布式锁-RedisLockRegistry源码分析

    前言 官网的英文介绍大概如下: Starting with version 4.0, the RedisLockRegistry is available. Certain components (f ...

随机推荐

  1. 【二】tensorflow调试报错、TF深度学习强化学习教学

    相关文章: [一]tensorflow安装.常用python镜像源.tensorflow 深度学习强化学习教学 [二]tensorflow调试报错.tensorflow 深度学习强化学习教学 [三]t ...

  2. 4.2 Inline Hook 挂钩技术

    InlineHook 是一种计算机安全编程技术,其原理是在计算机程序执行期间进行拦截.修改.增强现有函数功能.它使用钩子函数(也可以称为回调函数)来截获程序执行的各种事件,并在事件发生前或后进行自定义 ...

  3. git操作 手写稿

  4. java线程池实现多任务并发执行

    Java线程池实现多任务并发执行 1️⃣ 创建一些任务来落地多任务并发执行 每一个数组里面的数据可以看成任务,或者是需要并发的业务接口, 数组与数组之间,可以看作为他们之间有血缘关系,简单来说就是: ...

  5. 穿透 SESSION 0 隔离

    注:这套程序在这里只是作为了解,博主本人并未测试,所有测试均来自文章最底层链接的原文章. 查看目标进程运行的SESSION位置 在实际开发过程中,可以通过Process Explorer 检查服务或程 ...

  6. 长沙IT技术圈百万年薪大佬?是否存在?

    引子 不知不觉,IT技术圈开始流传起"百万年薪"的故事,有人问我,长沙有百万大佬么?其实我也不知道. 一 背景 长沙自古以来就是文风鼎盛之地,在今天也同样如此. 目前长沙有211. ...

  7. Power BI 6 DAY

    Power BI 数据建模与数据汇总分析 层级关系 跨表取字段时类型二可用 父子级关系条件 一个父级下对应多个子级值 一个子级值只属于一个父级 跨表取字段的条件:维度连接用关键字段间是父子级关系时,可 ...

  8. UVA1108 Mining Your Own Business 题解

    题目传送门 题意 在一个无向图上选择尽量少的点涂黑,使得删除任意一个点后,每个连通分量里都至少有一个黑点(多组数据). 正文 观察题意,发现这是个 Tarjan 求点双连通分量的板子. 考虑在求点双连 ...

  9. 【JS】一个思路搞定三道Promise并发编程题,手摸手教你实现一个Promise限制器

    壹 ❀ 引 之前在整理手写Promise相关资料时,在文章推荐区碰巧看到了一道手写Promise并发控制调度器的笔试题(大厂可能爱考),结果今天同事又正好问了我一个关于Promise调度处理的场景问题 ...

  10. NC19782 Tree

    题目链接 题目 题目描述 修修去年种下了一棵树,现在它已经有n个结点了. 修修非常擅长数数,他很快就数出了包含每个点的连通点集的数量. 澜澜也想知道答案,但他不会数数,于是他把问题交给了你. 输入描述 ...