引子: 解决缓存击穿问题

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. SqlSugar分组查询

    一.分组查询和使用 1.1 语法 只有在聚合对象需要筛选的时候才会用到Having,一般分组查询用不到可以去掉   var list = db.Queryable<Student>()   ...

  2. 2.5 Windows驱动开发:DRIVER_OBJECT对象结构

    在Windows内核中,每个设备驱动程序都需要一个DRIVER_OBJECT对象,该对象由系统创建并传递给驱动程序的DriverEntry函数.驱动程序使用此对象来注册与设备对象和其他系统对象的交互, ...

  3. UIWindow的概念与使用

    UIWindow的作用 UIWindow是UIView的子类用于显示程序内容.每一个UIView想要将内容显示到屏幕上都需要依赖于一个UIWindow. iOS应用程序要想正常运行至少要有一个UIWi ...

  4. 如何在 macOS Sonoma 虚拟机中安装 VMware Tools

    vmware-tools VMware Tools 简介 VMware Tools 中包含一系列服务和模块,可在 VMware 产品中实现多种功能,从而使用户能够更好地管理客户机操作系统,以及与客户机 ...

  5. Eclipse安装配置、卸载教程(Windows版)

    Eclipse是一个开放源代码的集成开发环境(IDE),最初由IBM公司开发,现在由Eclipse基金会负责维护.它是一个跨平台的工具,可以用于开发多种编程语言,如Java.C/C++.Python. ...

  6. JS leetcode 至少是其他数字的两倍的最大数 解答思路分析

    壹 ❀ 引 刷leetcode的第二天,那么今天做的也是一道难度为简单的题目至少是其他数字的两倍的最大数,老规矩,先说说我的实现思路后,再来分析优质答案,原题如下: 在一个给定的数组nums中,总是存 ...

  7. MindSponge分子动力学模拟——定义Collective Variables

    技术背景 在前面的几篇博客中,我们介绍了MindSponge分子动力学模拟框架的基本安装和使用和MindSponge执行分子动力学模拟任务的方法.这里我们介绍一个在增强采样领域非常常用的工具:Coll ...

  8. Laravel入坑指南(8)——控制台程序

    我们知道,php代码不仅可以用web的形式对外提供服务,同时也可以在命令行下执行. 对于原生的php来说,假设我们有一个php文件,名为Command.php,如果想要在控制台下执行这个文件,那么我们 ...

  9. [技术选型与调研] 流程引擎(工作流引擎|BPM引擎):Activiti、Flowable、Camunda

    1 概述:流程与流程引擎 低代码平台.办公自动化(OA).BPM平台.工作流系统均需要[流程引擎]功能 BPM平台与工作流系统的区别,参见本文档:3.2 章节 流程引擎是任务分配软件(例如业务流程管理 ...

  10. C. Sum of Substrings题解

    C. Sum of Substrings 题目大概意思,给你一个01串,求和最小,其中和是该串所有相邻字符所组成的十进制数的和. 如:0110, sum = 01 + 11 + 10 = 22. 通过 ...