通用多级缓件组件
背景
业界第三方缓存框架一般为redis,本地缓地ehcache或guava,一般通过spring提供的restTemplate操作缓存
然而这样会存在以下问题:
- 与缓存中间件强耦合
- 需手动整合多级缓存
- 不支持注解
- 数据更新时无法自动刷新缓存
- 存在缓存穿透、缓存击穿、缓存雪崩风险
- 日志不足
改造方案
基于上述问题,针对每点问题提出修复/优化方案:
1、与缓存中间件强耦合
对外仅暴露一个接口cacheService操作缓存,调用方无需关心内部实现,但需要通过配置开启第三方缓存

2、需手动整合多级缓存
自动整合两级缓存,redis + guava
3、不支持注解
增加自定义注解
4、数据更新时无法自动刷新缓存(未实现)
使用阿里开源框架canal监听mysql的binlog日志,将变更通过MQ广播到消费端,由消费端实时更新缓存:
1)MQ划分两个组:集群组与机器组,分别用于处理第三方缓存刷新(只需一台机器处理)和本地缓存刷新(每台机器都要处理)
2)canal收到binlog变更后,发送一条消息到集群组
3)收到消息的机器先判断缓存是否开启了本地缓存,若是则向机器组发送一条消息,然后判断是否开启了第三方缓存,若有则刷新最新数据
4)收到本地缓存消息的机器刷新本地缓存最新数据
5、存在缓存穿透、缓存击穿、缓存雪崩风险
缓存穿透:增加NULL值,缓存较短的时间
缓存击穿:增加锁同步数据获取过程
缓存雪崩:增加预警参数提前异步刷新,热点缓存监控
6、日志不足
增加统一日志,方便排查问题
详细设计
模块组成:

类图:

配置类
支持两级缓存:一级为本地缓存,使用guava实现;二级为分布式缓存,使用redis + redisson实现
二级缓存需要添加分布式服务配置,而一级缓存在缓存单位中配置即可,实时更新依赖MQ服务
1、服务配置
redisson配置类:RedissonConfiguration,用于redis相关配置
详细配置项参考附录
2、缓存单位配置
同一前缀为一个缓存单位,新增配置文件cache-config.yml(参考cache-config-demo.yml)对缓存参数进行定义,服务启动后会解析该文件,将配置保存到CacheConfiguration中
注意:支持依赖jar内配置cache-config.yml,若key冲突则在启动时报错
caches:# 业务空间,非必填application: mc# L1并发级别,即同时支持多少个线程,非必填l1ConcurrencyLevel: 10# 单个缓存配置cache:# 前缀,必填,唯一prefix: user_# 版本,非必填,用于缓存结构改变后升级reload最新内容version: v1# 缓存时间,必填,支持d/h/m/s,默认sexpired: 5m# 预警时间,非必填,支持d/h/m/s,默认s,用于提前更新alarm: 2m# 序列化方式,非必填,支持hessian/jdk,默认hessianserializer: hessian# 开启一级缓存,非必填,一二级至少开启其一L1:# 初始容量,非必填,Guava扩容代价大,尽量指定所需容量initialCapacity: 100# 最大容量,必填maximumSize: 1000# 并发级别,非必填,优先级高于l1ConcurrencyLevelconcurrencyLevel: 10# 开启二级缓存,非必填,一二级至少开启其一L2:# 指定配置名称,必填(否则无法解析为对象),用于多分布式缓存源,为空使用默认配置ref: xxxConfig# 实时更新配置,非必填(未实现)reload:# 订阅的表,必填,多个用逗号分隔table: user, lesson# reload操作,非必填,UPDATE(更新)/DELETE(删除,默认)/RELOAD(重新刷新)operation: UPDATE# key拼接方式,非必填,数据库记录用小括号作动态替换key: _(id)
模块服务类
1、存储实体
实体字段如下:
public class CacheEntity implements Serializable {private static final long serialVersionUID = 1L;// 真实内容,通过hession/jdk进行序列化/反序列化private String value;// 缓存时间private long cacheTime;// 预警时间private long alarmTime;
}
2、缓存操作
包括常规的get()、set()、add()、del()外,增加带LoadingCache功能的get方法,提供分布式锁方法DLock()、unDLock(),由redisson实现
public <T> T get(String prefix, String key, LoadingCache loader, Object ... params) {CacheEntity entity = null;T value = null;// 获取缓存单位配置Config config = CacheConfiguration.getConfig(prefix);if (config.hasL1() != null) {// 获取L1结果entity = getL1();}if (entity == null) {if (config.hasL2() != null) {// 获取L2结果entity = getL2();}}if (entity == null && loader != null) {// 获取数据value = loader.load(params);if (value != null) {// 设置缓存set(prefix, key, value);} else {// 设置NULL值setNull(prefix, key);}return value;}return serializer.deserialize(value);
}
3、提前更新
缓存未到expired但已到alarm时,在返回内容之前开启异步线程提前更新缓存内容
private void alarmUpdate(Config config, CacheEntity entity, LoadingCache loader) {long time = System.currentTimeMillis() + entity.getCacheTime();if (time > entity.getAlarmTime()) {T value = loader.load();if (value != null) {// 设置缓存set(prefix, key, value);}}
}
4、注解配置
支持在方法上增加自定义注解,在调用时自动走缓存
// 获取单个缓存
@MethodCache("user_")
public List getList(int id, String name) {// ...
}
// 获取批量缓存
// 注意第一个参数必须为key的list集合,返回以key分组
@MultipleMethodCache(CacheServiceTest.l1Prefix)
public Map<String, CacheTestB> getMultiAutoIncrement(List<String> keys, String value, int num) {// ...
}
注意:只能增强由spring管理的类方法
5、实时更新(未实现)
使用阿里开源框架canal监听mysql的binlog日志,将变更通过MQ广播到消费端,具体参考:Canal Kafka RocketMQ QuickStart
服务收到消息后,对消息进行解析处理
public boolean consumer(byte[] bytes) {Message message = JsonUtil.fromStr(bytes, Message.class);String table = message.getTable();// 获取reload配置ReloadConfig reloadConfig = CacheConfiguration.getReloadConfig(table);if (reloadConfig != null) {String key = reloadConfig.getKey();if (reloadConfig.getOperation() == DELETE) {// 删除缓存del(key);} else {// 更新缓存update(key, message);}}
}
6、其它
- 增加debug级别日志打印
- 增加是否使用缓存配置,用于测试用例
待实现功能
- 目前仅支持byte数组存储,后期将开放redis其它类型的实现(部分redis独有的类型geo等如真有需求,是否考虑直接提供client?这样子就增加了耦合性)
- 基于canal自动刷新缓存
- 热点数据统计
附录
源码
基于spring boot通用多级缓件组件: 业界第三方缓存框架一般为redis,本地缓地ehcache或guava,一般通过spring提供的restTemplate操作缓存,然而这样会存在一些问题,期望通过此组件解决问题
redis配置
以redisson作为前缀,开启redis配置:redisson.open.flag=true
单机版redis
#单Redis节点模式
redisson.singleServerConfig.address=127.0.0.1:6379
集群模式
集群模式除了适用于Redis集群环境,也适用于任何云计算服务商提供的集群模式,例如AWS ElastiCache集群版、Azure Redis Cache和阿里云(Aliyun)的云数据库Redis版。
#集群模式
redisson.model=CLUSTER
#redis机器.一直累加下去
redisson.multiple-server-config.node-addresses[0]=127.0.0.1:6379
redisson.multiple-server-config.node-addresses[1]=127.0.0.1:6380
redisson.multiple-server-config.node-addresses[2]=127.0.0.1:6381
云托管模式
云托管模式适用于任何由云计算运营商提供的Redis云服务,包括亚马逊云的AWS ElastiCache、微软云的Azure Redis 缓存和阿里云(Aliyun)的云数据库Redis版
#云托管模式
redisson.model=REPLICATED
#redis机器.一直累加下去
redisson.multiple-server-config.node-addresses[0]=127.0.0.1:6379
redisson.multiple-server-config.node-addresses[1]=127.0.0.1:6380
redisson.multiple-server-config.node-addresses[2]=127.0.0.1:6381
哨兵模式
redisson.model=SENTINEL
#主服务器的名称是哨兵进程中用来监测主从服务切换情况的。
redisson.multiple-server-config.master-name="mymaster"
#redis机器.一直累加下去
redisson.multiple-server-config.node-addresses[0]=127.0.0.1:6379
redisson.multiple-server-config.node-addresses[1]=127.0.0.1:6380
redisson.multiple-server-config.node-addresses[2]=127.0.0.1:6381
主从模式
redisson.model=MASTERSLAVE
#第一台机器就是主库.其他的为从库
redisson.multiple-server-config.node-addresses[0]=127.0.0.1:6379
redisson.multiple-server-config.node-addresses[1]=127.0.0.1:6380
redisson.multiple-server-config.node-addresses[2]=127.0.0.1:6381
属性列表
基本都是官方参数.我将参数整合了下.分为 公共参数,单例模式参数,集群模式参数
1.公共参数
| 属性名 | 默认值 | 备注 |
| redisson.name | default | 配置名称,必须保证唯一,否则配置将被覆盖 |
| redisson.password | 用于节点身份验证的密码。 | |
| redisson.attemptTimeout | 10000L | 等待获取锁超时时间,-1则是一直等待 |
| redisson.lockModel | 单个key默认 多个key默认 | 锁的模式.如果不设置, REENTRANT(可重入锁),FAIR(公平锁),MULTIPLE(联锁),REDLOCK(红锁),READ(读锁), WRITE(写锁) |
| redisson.model | SINGLE | 集群模式:SINGLE(单例),SENTINEL(哨兵),MASTERSLAVE(主从),CLUSTER(集群),REPLICATED(云托管) |
| redisson.codec | org.redisson.codec.JsonJacksonCodec | Redisson的对象编码类是用于将对象进行序列化和反序列化,以实现对该对象在Redis里的读取和存储 |
| redisson.threads | 当前处理核数量 * 2 | 这个线程池数量被所有RTopic对象监听器,RRemoteService调用者和RExecutorService任务共同共享。 |
| redisson.nettyThreads | 当前处理核数量 * 2 | 这个线程池数量是在一个Redisson实例内,被其创建的所有分布式数据类型和服务,以及底层客户端所一同共享的线程池里保存的线程数量。 |
| redisson.transportMode | NIO | TransportMode.NIO,TransportMode.EPOLL - 需要依赖里有netty-transport-native-epoll包(Linux) TransportMode.KQUEUE - 需要依赖里有 netty-transport-native-kqueue包(macOS) |
| redisson.idleConnectionTimeout | 10000 | 如果当前连接池里的连接数量超过了最小空闲连接数,而同时有连接空闲时间超过了该数值,那么这些连接将会自动被关闭,并从连接池里去掉。时间单位是毫秒 |
| redisson.connectTimeout | 10000 | 同任何节点建立连接时的等待超时。时间单位是毫秒。 |
| redisson.timeout | 3000 | 等待节点回复命令的时间。该时间从命令发送成功时开始计时。 |
| redisson.retryAttempts | 3 | 如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误。如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时。 |
| redisson.retryInterval | 1500 | 在一条命令发送失败以后,等待重试发送的时间间隔。时间单位是毫秒。 |
| redisson.subscriptionsPerConnection | 5 | 每个连接的最大订阅数量。 |
| redisson.clientName | 在Redis节点里显示的客户端名称。 | |
| redisson.sslEnableEndpointIdentification | true | 开启SSL终端识别能力。 |
| redisson.sslProvider | JDK | 确定采用哪种方式(JDK或OPENSSL)来实现SSL连接。 |
| redisson.sslTruststore | 指定SSL信任证书库的路径。 | |
| redisson.sslTruststorePassword | 指定SSL信任证书库的密码。 | |
| redisson.sslKeystore | 指定SSL钥匙库的路径。 | |
| redisson.sslKeystorePassword | 指定SSL钥匙库的密码。 | |
| redisson.lockWatchdogTimeout | 30000 | 监控锁的看门狗超时时间单位为毫秒。该参数只适用于分布式锁的加锁请求中未明确使用leaseTimeout参数的情况。如果该看门口未使用lockWatchdogTimeout去重新调整一个分布式锁的lockWatchdogTimeout超时,那么这个锁将变为失效状态。这个参数可以用来避免由Redisson客户端节点宕机或其他原因造成死锁的情况。 |
| redisson.keepPubSubOrder | true | 通过该参数来修改是否按订阅发布消息的接收顺序出来消息,如果选否将对消息实行并行处理,该参数只适用于订阅发布消息的情况。 |
单例模式参数
| 属性名 | 默认值 | 备注 |
| redisson.singleServerConfig.address | 服务器地址,必填ip:port | |
| redisson.singleServerConfig.database | 0 | 尝试连接的数据库编号。 |
| redisson.singleServerConfig.subscriptionConnectionMinimumIdleSize | 1 | 用于发布和订阅连接的最小保持连接数(长连接)。Redisson内部经常通过发布和订阅来实现许多功能。长期保持一定数量的发布订阅连接是必须的。 |
| redisson.singleServerConfig.subscriptionConnectionPoolSize | 50 | 用于发布和订阅连接的连接池最大容量。连接池的连接数量自动弹性伸缩。 |
| redisson.singleServerConfig.connectionMinimumIdleSize | 32 | 最小保持连接数(长连接)。长期保持一定数量的连接有利于提高瞬时写入反应速度。 |
| redisson.singleServerConfig.connectionPoolSize | 64 | 连接池最大容量。连接池的连接数量自动弹性伸缩。 |
| redisson.singleServerConfig.dnsMonitoringInterval | 5000 | 用来指定检查节点DNS变化的时间间隔。使用的时候应该确保JVM里的DNS数据的缓存时间保持在足够低的范围才有意义。用-1来禁用该功能。 |
集群模式
| 属性名 | 默认值 | 备注 |
| redisson.multiple-server-config.node-addresses | 服务器节点地址.必填 | |
| redisson.multiple-server-config.loadBalancer | org.redisson.connection.balancer.RoundRobinLoadBalancer | 在多Redis服务节点的环境里,可以选用以下几种负载均衡方式选择一个节点: |
| redisson.multiple-server-config.slaveConnectionMinimumIdleSize | 32 | 多从节点的环境里,每个 从服务节点里用于普通操作(非 发布和订阅)的最小保持连接数(长连接)。长期保持一定数量的连接有利于提高瞬时读取反映速度。 |
| redisson.multiple-server-config.slaveConnectionPoolSize | 64 | 多从节点的环境里,每个 从服务节点里用于普通操作(非 发布和订阅)连接的连接池最大容量。连接池的连接数量自动弹性伸缩。 |
| redisson.multiple-server-config.masterConnectionMinimumIdleSize | 32 | 多节点的环境里,每个 主节点的最小保持连接数(长连接)。长期保持一定数量的连接有利于提高瞬时写入反应速度。 |
| redisson.multiple-server-config.masterConnectionPoolSize | 64 | 多主节点的环境里,每个 主节点的连接池最大容量。连接池的连接数量自动弹性伸缩。 |
| redisson.multiple-server-config.readMode | SLAVE | 设置读取操作选择节点的模式。 可用值为: SLAVE - 只在从服务节点里读取。 MASTER - 只在主服务节点里读取。 MASTER_SLAVE - 在主从服务节点里都可以读取。 |
| redisson.multiple-server-config.subscriptionMode | SLAVE | 设置订阅操作选择节点的模式。 可用值为: SLAVE - 只在从服务节点里订阅。 MASTER - 只在主服务节点里订阅。 |
| redisson.multiple-server-config.subscriptionConnectionMinimumIdleSize | 1 | 用于发布和订阅连接的最小保持连接数(长连接)。Redisson内部经常通过发布和订阅来实现许多功能。长期保持一定数量的发布订阅连接是必须的。 redisson.multiple-server-config.subscriptionConnectionPoolSize |
| redisson.multiple-server-config.dnsMonitoringInterval | 5000 | 监测DNS的变化情况的时间间隔。 |
| redisson.multiple-server-config.scanInterval | 1000 | (集群,哨兵,云托管模特特有) 对Redis集群节点状态扫描的时间间隔。单位是毫秒。 |
| redisson.multiple-server-config.database | 0 | (哨兵模式,云托管,主从模式特有)尝试连接的数据库编号。 |
| redisson.multiple-server-config.masterName | (哨兵模式特有)主服务器的名称是哨兵进程中用来监测主从服务切换情况的。 |
多redis源配置
以multiple.redisson为前缀,sources为多源,以key/value配置,value与上面的配置一致(去掉redission)
例:
multiple.redisson.sources.test2.singleServerConfig.address=10.8.104.71:6379
multiple.redisson.sources.test2.threads=1
即ref=test2时使用上述配置
相关文章:
通用多级缓件组件
背景 业界第三方缓存框架一般为redis,本地缓地ehcache或guava,一般通过spring提供的restTemplate操作缓存 然而这样会存在以下问题: 与缓存中间件强耦合需手动整合多级缓存不支持注解数据更新时无法自动刷新缓存存在缓存穿透、缓存击穿、缓…...
MindIE Service服务化集成部署通义千问Qwen模型
一、昇腾开发者平台申请镜像 登录Ascend官网昇腾社区-官网丨昇腾万里 让智能无所不及 二、登录并下载mindie镜像 #登录docker login -u XXX#密码XXX#下载镜像docker pull XXX 三、下载Qwen的镜像 使用wget命令下载Qwen1.5-0.5B-Chat镜像,放在/mnt/Qwen/Qwen1.5-…...
chrome 接口请求等待时间(installed 已停止)过长问题定位
参考: 解决实际项目中stalled时间过久的问题 背景: 测试反馈系统开 6 个标签页后, 反应变的很慢 定位: 看接口请求瀑布流, 已停止时间很长, 后端返回速度很快, 确定是前端的问题 推测是并发请求窗口数量的问题, 屏蔽部分一直 pending 的接口, 发现速度正常了, 搜到上面的参…...
HDialog特殊动画效果
基于HDialog的特殊动画效果实现 业务场景 在开发过程中直接使用HDialog所展现的效果很快,同时不能够与用户所点击位置进行交互,会造成用户的体验观感不够好。因此需要实现一种能够从用户点击按钮位置以可变动画效果所展现的Dialog效果。 工作原理及实…...
基因组挖掘指导天然药物分子的发现-文献精读34
基因组挖掘指导天然药物分子的发现 摘要 天然产物是临床药物的主要来源,也是新药研发过程中先导化合物结构设计和优化的灵感源泉。但传统策略天然药源分子的发现却遭遇了瓶颈,新颖天然产物的数量逐渐无法满足现代药物开发的需求和应对全球多药耐药的威胁…...
hcip学习 DHCP中继
DHCP 中继 在可能收到 DHCP Discover 报文的接口配置 DHCP 中继, 指明 DHCP 服务器的地址,然后将 DHCP 发现报文以单播的形式送到 DHCP 服务器上 DHCP 中继报文的源地址和目标地址怎么确定 1、源地址:收到 Discover 报文的接口地址 2、目…...
[Mysql-函数、索引]
目录 函数: 日期函数 字符串函数 数学函数 聚合函数 索引: 索引分类 慢查询 创建索引 函数: MySQL函数,是一种控制流程函数,属于数据库用语言。 MySQL常见的函数有: 数学函数 用作常规的数学运…...
org.eclipse.jgit 简单总结
org.eclipse.jgit 是一个用于处理 Git 版本控制系统的纯 Java 库。它允许你读取和写入 Git 仓库,执行如克隆、拉取、推送、提交等操作。下面我将通过几个例子来展示如何使用 org.eclipse.jgit 进行一些常见的 Git 操作。 1. 克隆仓库 克隆一个远程 Git 仓库到本地目…...
Fork软件笔记:一键拉取仓库所有模块
Fork是一个好用的git工具,只是没有中文而已(不过不用翻译也能看使用)。 工具下载地址:https://fork.dev/ 界面展示: 当项目中仓库模块比较多时,可以看到每个模块都是一个分页,每一个都要手动切换…...
常见的锂电保护芯片 单节锂电保护/双节锂电保护芯片
目前外出贸易的要求不断增多,出口的产品基本上都需要带上锂电保护芯片 以下是常见的单节锂电保护芯片的选型 包括了市面上大部分的可用型号。 锂电保护芯片的脚位上面基本都是通用,可以直接替代 双节的锂电保护使用情况较少,需要外置MOS管调节…...
初识Java(六)
一、String类 1、类中有操作字符串的方法 查找:找到某个字符是字符串内的第几个:charAt;找到某个字符在字符串内第一次出现的下标:index 替换:替换所有:replaceAll;替换首个:repla…...
Spring-原理篇-DispatcherServlet 初始化 怎么和IOC进行了打通?
委托模式的体现,在初始化醒目的时候Spring MVC为我们提供了一个DispatcherServlet,映射了所有的路径,所有的请求都会先到达这里然后被转发到具体的Controller 进行处理,此文来探索一下,DispatcherServlet 初始化的时候…...
关于swift- OC混编使用Pod遇到的2个错误
错误1 Cannot find interface declaration for UITableViewCell, superclass of "DEFUITalbleViewCell" Cannot find interface declaration for UIView, superclass of "DefUIView" Cannot find interface declaration for 系统类, superclass of "自…...
Golang | Leetcode Golang题解之第290题单词规律
题目: 题解: func wordPattern(pattern string, s string) bool {word2ch : map[string]byte{}ch2word : map[byte]string{}words : strings.Split(s, " ")if len(pattern) ! len(words) {return false}for i, word : range words {ch : patt…...
【Django5】模型定义与使用
系列文章目录 第一章 Django使用的基础知识 第二章 setting.py文件的配置 第三章 路由的定义与使用 第四章 视图的定义与使用 第五章 二进制文件下载响应 第六章 Http请求&HttpRequest请求类 第七章 会话管理(Cookies&Session) 第八章 文件上传…...
HTML--JavaScript操作DOM对象
目录 本章目标 一.DOM对象概念 编辑 二.节点访问方法 常用方法: 层次关系访问节点 三.节点信息 四.节点的操作方法 操作节点的属性 创建节点 删除替换节点 五.节点操作样式 style属性 class-name属性 六.获取元素位置 总结 本章目标 了解DOM的分类和节…...
Redis 缓存
安装 安装 Redis 下载: Releases tporadowski/redis (github.com) winr ----services.msc-----将redis 设置为手动(只是学习,如果经常用可以设置为自动) 安装 redis-py 库 pip install redis-py Redis 和 StrictRedis redis-py 提供 Redis 和 Str…...
Prozyme糖样本检测平台--GlykoPrep® Rapid N-Glycan Preparation with APTS
单克隆抗体已成为生物制药行业具有潜力的新兴蛋白候选药物。其药物研发流程包括一系列精细的控制和评估步骤,需要仔细、严格地监测目标化合物的治疗稳定性及有效性。因此,在商业化前的每个阶段对单克隆抗体进行全面表征是极其有益的。在大量研究成熟的蛋…...
力扣面试题(一)
1、给你两个字符串 word1 和 word2 。请你从 word1 开始,通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长,就将多出来的字母追加到合并后字符串的末尾。 char * mergeAlternately(char * word1, char * word2){int len1 strlen(word1);i…...
Python 输入输出
重点内容: 1、梳理掌握输入和输出函数的应用。 2、熟练使用int() float() str()等函数进行数据转换 3、常用转义字符在数据输入、输出中的应用 4、熟练使用ljust()、center()、rjust()等方法对字符位置进行控制。 5、灵活应用ASCII码、字母、数字及特殊字符解决…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
