当前位置: 首页 > news >正文

实战——缓存的使用

文章目录

  • 前言
  • 概述
  • 实践
    • 一、缓存数据一致
      • 1.更新缓存类
      • 2.删除缓存类
    • 二、项目实践(商城项目)
      • 缓存预热
      • 双缓存机制


前言

对于我们日常开发的应用系统。由于MySQL等关系型数据库读写的并发量是有一定的上线的,当请求量过大时候那数据库的压力一定会上来。

所以采用 MySQL+Redis 这对经典组合来解决高并发问题的。Redis 作为 MySQL 的前置缓存,可以应对绝大部分查询请求,从而在很大程度上缓解 MySQL 并发请求的压力,但是不能一说到缓存脑海中就只有 Redis,这无论在工作还是面试中都不合适,所以我们先全面了解缓存。

注意:缓存不止有redis,需要全面的了解缓存


概述

  • 缓存大体可以分为三类(意思就是在整个系统中我们每一层都是有缓存的):
    • 客户端缓存;
    • 服务端缓存;
    • 网络中的缓存。
  • 根据规模和部署方式缓存也可以分为:
    • 单体缓存;
    • 缓存集群;
    • 分布式缓存。

我们通过对每一层进行缓存来提高系统的稳定性和效率。


实践

一、缓存数据一致

不论是本地缓存还是redis,我们的基本思路就是:当缓存没有命中的时候,我们就去数据库查询,然后直接放到缓存中,供下次查询以加快查询效率。
但是我们在修改数据的时候,就可能造成数据库和缓存数据一致性的问题。
有好几种解决方案,(两大类:一:更新;二:删除)1. 先更新缓存,再更新数据库2. 先更新数据库,再更新缓存3、先删除缓存,后更新数据库4、先更新数据库,后删除缓存

1.更新缓存类

不论先更新数据库还是先更新缓存,这两种方案都不可取。原因就是不论我们先更新谁后更新谁,就会导致我们前面更新成功后,后面更新的那个万一挂了,我们就难以判断是否成功。所以这类更新的方案,对我们来说都是不可取的。

2.删除缓存类

  • 2.1 先删缓存,后更新数据库
    • 问题:

      • 线程A删除缓存后,更新DB,但是DB的事务并没有提交,线程B进来访问。那么就会重新更新缓存(造成缓存和DB数据不一致)
        在这里插入图片描述
    • 解决方案

      • 使用延时双删
        • 1.先删除缓存,再去更新DB
        • 2.当DB更新成功之后,延时个1s,再删除一次缓存

  • 2.2 先更新数据库,后删除缓存(开发中常用的策略)

    • 问题:

      • 在缓存失效的且并发的时候会发生(虽然概率比较小但是还是会发生,因为概率小所以我们在开发中常用)
        在这里插入图片描述
    • 解决方案:

      • 1.设置缓存失效时间。
      • 2.异步延时删除机制(问题在于当我们删除缓存的时候万一失败需要补偿机制来保证缓存一定删除)
        异步补偿删除方案一:
        在这里插入图片描述

    异步补偿删除方案二:

    由于方案一中的业务代码的耦合性较高。使用订阅数据库的binlog

在这里插入图片描述

说到底就是通过数据库的 binlog 来异步淘汰 key,利用工具(canal)将 binlog
日志采集发送到 MQ 中,然后通过 ACK 机制确认处理删除缓存。
先更新 DB,后删除缓存,这种方式,被称为 Cache Aside Pattern,属于缓存更新的设计模式之一。(这是一种最为标准的方案)


二、项目实践(商城项目)

我这边以京东商城为例

在这里插入图片描述

像这样的商城首页一定是并发最高的地方,如果我们每次都去数据库查询,很显然是不满足我们高并发的要求的。

像这种任何用户看到的都是一样的结果的数据,在缓存中的命中率是比较高得,所以我们可以考虑引入缓存的方式。
并且我们这里完全可以分为多个key,如促销,轮播图,标签等。

  1. 首页缓存使用
// 大佬的代码例子public HomeContentResult getFromRemote(){List<PmsBrand> recommendBrandList = null;List<SmsHomeAdvertise> smsHomeAdvertises = null;List<PmsProduct> newProducts  = null;List<PmsProduct> recommendProducts  = null;HomeContentResult result;/*从redis获取*/if(promotionRedisKey.isAllowRemoteCache()){recommendBrandList = redisOpsUtil.getListAll(promotionRedisKey.getBrandKey(), PmsBrand.class);smsHomeAdvertises = redisOpsUtil.getListAll(promotionRedisKey.getHomeAdvertiseKey(), SmsHomeAdvertise.class);newProducts = redisOpsUtil.getListAll(promotionRedisKey.getNewProductKey(), PmsProduct.class);recommendProducts = redisOpsUtil.getListAll(promotionRedisKey.getRecProductKey(), PmsProduct.class);}/*redis没有则从微服务中获取*/if(CollectionUtil.isEmpty(recommendBrandList)||CollectionUtil.isEmpty(smsHomeAdvertises)||CollectionUtil.isEmpty(newProducts)||CollectionUtil.isEmpty(recommendProducts)) {result = promotionFeignApi.content(0).getData();}else{result = new HomeContentResult();result.setBrandList(recommendBrandList);result.setAdvertiseList(smsHomeAdvertises);result.setHotProductList(recommendProducts);result.setNewProductList(newProducts);}return result;}

redis 初始化key代码初始化注解@PostConstruct

@Service
@Slf4j
public class PromotionRedisKey {// 配置@Value ("${namespace.promotion:prmtd}")private String promotionNamespace;@Value ("${promotion.brand:br}")private String brand;@Value ("${promotion.newProduct:np}")private String newProduct;@Value ("${promotion.recProduct:rp}")private String recProduct;@Value ("${promotion.homeAdvertise:hd}")private String homeAdvertise;@Value ("${promotion.seckill:sk}")private String secKill;// 需要初始化的keyprivate String brandKey;private String newProductKey;private String recProductKey;private String homeAdvertiseKey;private String secKillKey;// 全局缓存控制开关@Value("${promotion.demo.allowLocalCache:true}")private boolean allowLocalCache;@Value("${promotion.demo.allowRemoteCache:true}")private boolean allowRemoteCache;/*** 该注解的方法在整个Bean初始化中的执行顺序:** Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的初始化方法)** 该注解的功能:当依赖注入完成后用于执行初始化的方法,并且只会被执行一次*/@PostConstructpublic void initKey(){brandKey = promotionNamespace + "." + brand;newProductKey = promotionNamespace + "." + newProduct;recProductKey = promotionNamespace + "." + recProduct;homeAdvertiseKey = promotionNamespace + "." + homeAdvertise;secKillKey = promotionNamespace + "." + secKill;StringBuilder logKeyStr = new StringBuilder();logKeyStr.append("[品牌推荐redis主键=").append(brandKey).append("] [新品推荐redis主键=").append(newProductKey).append("] [人气推荐redis主键=").append(recProductKey).append("] [轮播广告redis主键=").append(homeAdvertiseKey).append("] [秒杀redis主键=").append(secKillKey).append("]");log.info("促销系统Redis主键配置:{}",logKeyStr);}public String getBrandKey() {return brandKey;}public String getNewProductKey() {return newProductKey;}public String getRecProductKey() {return recProductKey;}public String getHomeAdvertiseKey() {return homeAdvertiseKey;}public String getSecKillKey() {return secKillKey;}public boolean isAllowLocalCache() {return allowLocalCache;}public boolean isAllowRemoteCache() {return allowRemoteCache;}
}

通过以上首页对缓存的使用,提出一些思考如下:
缓存一定是离用户越近越好,依据这个原则,首页还有优
化的空间,从上面的访问路径可以看到,首页服务需要到 Redis 集群中获得数据用以展示,能不能将缓存的数据再提前呢?于是我们在首页服务内引入了应用级缓存 Caffeine。

什么叫近???
在这里插入图片描述

TIPS:Caffeine 基于 Google 的 Guava Cache,提供一个性能卓越的本地缓存(local cache) 实现, 也是 SpringBoot 内置的本地缓存实现,有资料表明 Caffeine性能是 Guava Cache 的 6 倍

Caffeine使用

@Configuration
public class CaffeineCacheConfig {@Bean(name = "promotion")// 缓存预热(指定bean名称)public Cache<String, HomeContentResult> promotionCache() {int rnd = ThreadLocalRandom.current().nextInt(10);return Caffeine.newBuilder()// 设置最后一次写入经过固定时间过期.expireAfterWrite(30 + rnd, TimeUnit.MINUTES)// 初始的缓存空间大小.initialCapacity(20)// 缓存的最大条数.maximumSize(100).build();}/*以双缓存的形式提升首页的访问性能,这个备份缓存其实设置为永不过期更好,* 可以作为首页的降级和兜底方案* 为了说明缓存击穿和分布式锁这里设置了一个过期时间*/@Bean(name = "promotionBak")public Cache<String, HomeContentResult> promotionCacheBak() {int rnd = ThreadLocalRandom.current().nextInt(10);return Caffeine.newBuilder()// 设置最后一次访问经过固定时间过期.expireAfterAccess(41 + rnd, TimeUnit.MINUTES)// 初始的缓存空间大小.initialCapacity(20)// 缓存的最大条数.maximumSize(100).build();}/*秒杀信息在首页的缓存*/@Bean(name = "secKill")public Cache<String, List<FlashPromotionProduct>> secKillCache() {int rnd = ThreadLocalRandom.current().nextInt(400);return Caffeine.newBuilder()// 设置最后一次写入经过固定时间过期.expireAfterWrite(500 + rnd, TimeUnit.MILLISECONDS)// 初始的缓存空间大小.initialCapacity(20)// 缓存的最大条数.maximumSize(100).build();}/*秒杀信息在首页的缓存备份,提升首页的访问性能*/@Bean(name = "secKillBak")public Cache<String, List<FlashPromotionProduct>> secKillCacheBak() {int rnd = ThreadLocalRandom.current().nextInt(400);return Caffeine.newBuilder()// 设置最后一次写入经过固定时间过期.expireAfterWrite(100 + rnd, TimeUnit.MILLISECONDS)// 初始的缓存空间大小.initialCapacity(20)// 缓存的最大条数.maximumSize(100).build();}
}
@Autowired@Qualifier("promotion") // 指定注入相关对象和上面的配置一致,就是在容器中拿对应的值private Cache<String, HomeContentResult> promotionCache;@Overridepublic HomeContentResult cmsContent(HomeContentResult content) {//获取推荐专题content.setSubjectList(homeDao.getRecommendSubjectList(0,4));return content;}/*处理首页推荐品牌和商品内容*/@Overridepublic HomeContentResult recommendContent(){/*品牌和产品在本地缓存中统一处理,有则视为同有,无则视为同无*/final String brandKey = promotionRedisKey.getBrandKey();final boolean allowLocalCache = promotionRedisKey.isAllowLocalCache();/*先从本地缓存中获取推荐内容*/HomeContentResult result = allowLocalCache ?promotionCache.getIfPresent(brandKey) : null;if(result == null){result = allowLocalCache ?promotionCacheBak.getIfPresent(brandKey) : null;}/*本地缓存中没有*/if(result == null){log.warn("从本地缓存中获取推荐品牌和商品失败,可能出错或禁用了本地缓存[allowLocalCache = {}]",allowLocalCache);// 去redis中去,没有再去DB中取result = getFromRemote();if(null != result) {promotionCache.put(brandKey,result);promotionCacheBak.put(brandKey,result);}}/* 处理秒杀内容*/final String secKillKey = promotionRedisKey.getSecKillKey();List<FlashPromotionProduct> secKills = secKillCache.getIfPresent(secKillKey);if(CollectionUtils.isEmpty(secKills)){secKills = secKillCacheBak.getIfPresent(secKillKey);}if(CollectionUtils.isEmpty(secKills)){/*极小的概率出现本地两个缓存同时失效的问题,从远程获取时,只从Redis缓存中获取,不从营销微服务中获取,避免秒杀的流量冲垮营销微服务*/secKills = getSecKillFromRemote();if(!CollectionUtils.isEmpty(secKills)) {secKillCache.put(secKillKey,secKills);secKillCacheBak.put(secKillKey,secKills);}else{/*Redis缓存中也没有秒杀活动信息,此处用一个空List代替,* 其实可以用固定的图片或信息,作为降级和兜底方案*/secKills = new ArrayList<FlashPromotionProduct>();}}result.setHomeFlashPromotion(secKills);// fixme CMS本次不予实现,设置空集合result.setSubjectList(new ArrayList<CmsSubject>());return result;}

缓存预热

缓存预热是为了防止,我们的项目刚发版,可能请求过多造成的数据库压力过大的情况。

使用Spring的启动化机制

@Component
@Slf4j
// 缓存预热,使用Spring启动化机制CommandLineRunner
public class preheatCache implements CommandLineRunner {@Autowiredprivate HomeService homeService;@Overridepublic void run(String... args) throws Exception {for (String str : args) {log.info("系统启动命令行参数: {}", str);}// 缓存预热,可以包括本地缓存或者redishomeService.preheatCache();}}

双缓存机制

在这里插入图片描述

为了数据的一致性,本地 Caffeine和redis设置了过期时间,Redis 集群中的数据也会在数据变动后被除。当数据同时过期的时候。可能有以下的情况本地缓存去redis去取数据;本地缓存取DB取数据。那么在耗时上就会产生毛刺现象。


为了避免以上情况,我们使用双缓存的结构。

    @Bean(name = "promotion")// 缓存预热public Cache<String, HomeContentResult> promotionCache() {int rnd = ThreadLocalRandom.current().nextInt(10);return Caffeine.newBuilder()// 设置最后一次写入经过固定时间过期.expireAfterWrite(30 + rnd, TimeUnit.MINUTES)// 初始的缓存空间大小.initialCapacity(20)// 缓存的最大条数.maximumSize(100).build();}备份缓存要随着主缓存的变动而变动/*以双缓存的形式提升首页的访问性能,这个备份缓存其实设置为永不过期更好,* 可以作为首页的降级和兜底方案* 为了说明缓存击穿和分布式锁这里设置了一个过期时间*/@Bean(name = "promotionBak")public Cache<String, HomeContentResult> promotionCacheBak() {int rnd = ThreadLocalRandom.current().nextInt(10);return Caffeine.newBuilder()// 设置最后一次访问经过固定时间过期.expireAfterAccess(41 + rnd, TimeUnit.MINUTES)// 初始的缓存空间大小.initialCapacity(20)// 缓存的最大条数.maximumSize(100).build();}

相关文章:

实战——缓存的使用

文章目录前言概述实践一、缓存数据一致1.更新缓存类2.删除缓存类二、项目实践&#xff08;商城项目&#xff09;缓存预热双缓存机制前言 对于我们日常开发的应用系统。由于MySQL等关系型数据库读写的并发量是有一定的上线的&#xff0c;当请求量过大时候那数据库的压力一定会上…...

2023年中职网络安全竞赛跨站脚本渗透解析-2(超详细)

跨站脚本渗透 任务环境说明:需求环境可私信博主! 服务器场景:Server2126(关闭链接)服务器场景操作系统:未知访问服务器网站目录1,根据页面信息完成条件,将获取到弹框信息作为flag提交;访问服务器网站目录2,根据页面信息完成条件,将获取到弹框信息作为flag提交;访问…...

Scala的简单使用

文章目录Scala的简单使用&#xff08;一&#xff09;交互模式1、命令行方式2、文件方式&#xff08;二&#xff09;编译模式1、创建源程序2、编译成字节码3、解释执行对象Scala的简单使用 Scala可以在交互模式和编译模式两种方式下运行 &#xff08;一&#xff09;交互模式 在…...

Java之前缀和算法

目录 一.前缀和 1.前缀和介绍 2.编程中的前缀和 二.一维数组的动态和 1.题目描述 2.问题分析 3.代码实现 三.除自身以外数组的乘积 1.题目描述 2.问题分析 3.代码实现 四.和为 K 的子数组 1.题目描述 2.问题分析 3.代码实现 五.形成两个异或相等数组的三元组数目…...

基于GIS计算降雨侵蚀力R因子

一、数据来源介绍 &#xff08;一&#xff09;行政边界数据 本文所用到的河北唐山行政边界数据来源于中国科学院资源环境科学与数据中心&#xff08;https://www.resdc.cn/Default.aspx&#xff09;。 &#xff08;二&#xff09;降水量数据 本文所用到的降水量数据来源于国家…...

大数据时代下的企业网络安全

在大数据技术迅猛发展的今天&#xff0c;网络安全问题已经发展成一个广受关注的热门研究方向。有人说&#xff0c;“大数据下&#xff0c;人人裸奔”&#xff0c;隐私保护、数据防护日益成为广大学者、企业研究的焦点。 面对这种安全威胁&#xff0c;企业必须实施一些有效的信…...

【跟我一起读《视觉惯性SLAM理论与源码解析》】第三章第四章 SLAM中常用的数学基础知识相机成像模型

齐次坐标能大大简化在三维空间中点、线、面表达方式和旋转、平移等操作在齐次坐标下&#xff0c;两个点的叉积结果可以表示一条直线l;也可以用两条直线的叉积结果表示它们的齐次坐标交点&#xff0c;关于叉积其实十四讲解释的还是比较清楚的&#xff0c;和李代数李群的关系可以…...

LeetCode 242. 有效的字母异位词

242. 有效的字母异位词 难度&#xff1a;easy\color{Green}{easy}easy 题目描述 给定两个字符串 sss 和 ttt &#xff0c;编写一个函数来判断 ttt 是否是 sss 的字母异位词。 注意&#xff1a; 若 sss 和 ttt 中每个字符出现的次数都相同&#xff0c;则称 sss 和 ttt 互为字…...

力扣mysql刷题记录

mysql刷题记录 刷题链接https://leetcode.cn/study-plan/sql/?progressjkih0qc mysql冲&#xff01;mysql刷题记录1699. 两人之间的通话次数1251. 平均售价1571. 仓库经理1445. 苹果和桔子1193. 每月交易 I1633. 各赛事的用户注册率1173. 即时食物配送 I1211. 查询结果的质量…...

Linux基础命令-lsof查看进程打开的文件

Linux基础命令-uptime查看系统负载 Linux基础命令-top实时显示系统状态 Linux基础命令-ps查看进程状态 文件目录 前言 一 命令的介绍 二 语法及参数 2.1 使用help查看命令的语法信息 2.2 常用参数 2.2.lsof命令-i参数的条件 三 命令显示内容的含义 3.1 FD 文件描述符的…...

常用电平标准

现在常用的电平标准有TTL CMOS LVTTL LVCMOS LVDS PCI等&#xff0c;下面简单介绍一下各自的供电电源、电平标准及注意事项数字电路中&#xff0c;由TTL电子元件组成电路使用的电平。电平是个电压范围。标准输出高电平(VOH): 2.4V标准输出低电平(VOL)&#xff1a;0.4V通常输出高…...

小程序开发注意点

1.组件样式隔离注意点 2.methods方法 3.自定义组件的properties参数 4.自定义组件的事件监听 5.纯数据字段 6.插槽 单个插槽 启用多插槽 使用多个插槽 7.属性绑定实现父传子功能 例如在这里有一个组件为<one></one>&#xff0c;那么可以在组件当中传入参数 &l…...

自行车出口欧盟CE认证,新版自行车标准ISO 4210:2023与ISO 8098:2023发布

2023年1月&#xff0c;国际标准化组织ISO发布了新版“自行车以及儿童自行车的测试标准”&#xff0c;即ISO 4210&#xff1a;2023以及ISO 8098:2023&#xff0c;用于取代了SO 4210&#xff1a;2015以及ISO 8098:2015。新版标准一经发布&#xff0c;立即生效。欧盟标准化委员会C…...

2020蓝桥杯真题回文日期 C语言/C++

题目描述 2020 年春节期间&#xff0c;有一个特殊的日期引起了大家的注意&#xff1a;2020 年 2 月 2 日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202&#xff0c;恰好是一个回文数。我们称这样的日期是回文日期。 有人表示 20200202 是 “千年一遇…...

postman入门到精通之【接口知识准备】(一)

postman入门到精通之【接口知识准备】&#xff08;一&#xff09; 目录&#xff1a;导读 前言 接口测试概念 接口测试 接口测试的原理 常用接口测试工具 接口测试基础知识 接口的定义 接口的分类 HTTP接口 Web Service接口 RESTful接口 HTTP请求 统一资源定位符&…...

【算法数据结构体系篇class07】:加强堆

一、手动改写堆&#xff08;非常重要&#xff09;&#xff01;系统提供的堆无法做到的事情&#xff1a;1&#xff09;已经入堆的元素&#xff0c;如果参与排序的指标方法变化&#xff0c;系统提供的堆无法做到时间复杂度O(logN)调整&#xff01;都是O(N)的调整&#xff01;2&am…...

Taro3.x 容易踩坑的点(阻止滚动穿透,弹框蒙层父级定位)

解决弹框滚动的时候&#xff0c;下层也会滚动问题》阻止滚动穿透(react,vue)案例描述&#xff1a;页面展示时需要滚动条才可以显示完整&#xff0c;但是当我们显示弹框的时候&#xff0c;即使不需要滚动条&#xff0c;但是页面仍然可以滚动&#xff0c;并且下层内容会随着滚动变…...

SpringBoot+ActiveMQ-发布订阅模式(消费端)

ActiveMQ消息中间件的发布订阅模式 主题 topictopic生产端案例(配合topic消费端测试)&#xff1a;SpringBootActiveMQ Topic 生产端ActiveMQ版本&#xff1a;apache-activemq-5.16.5案例源码:SpringBootActiveMQ-发布订阅DemoSpringBoot集成ActiveMQ Topic消费端的pom.xml<?…...

vscode下使用arduino插件开发ESP32 Heltec WiFi_Kit_32_V3

下载vsCode 添加 arduino 插件 在Arduino IDE 中添加开发板&#xff0c;注意只能用右侧的开发板管理器添加&#xff0c;自己下载之后复制进去的IDE认&#xff0c;但是vsCode不认&#xff0c;搜索ESP32 第一个库里面只有到V2的&#xff0c;没有V3&#xff0c;要安装下面那个 H…...

吐血整理AutoSAR Com-Stack 的配置【基于ETAS】

总目录链接>> AutoSAR入门和实战系列总目录 文章目录01.软件组件和系统说明02.基本软件配置03.系统数据映射04.代码生成05.代码整合06.测试下图显示了基于 AUTOSAR 的 ECU SW 的结构。纵观BSW&#xff0c;大体分为三层。三层模块中&#xff0c;与通信相关的模块称为通信…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...