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

简洁易懂:源码+实战讲解Redisson并发锁及看门狗自动续期

1 缘起

有一次同事问Redisson存储的键是否为hash?
我当时,没有看Redisson的相关源码,只知道应用,
所以没有办法回答,于是开始看看Redisson实现的源码,
顺便写了一个单机Redisson测试,
发现Redisson有看门狗功能,但是,不是所有的方法都会触发,
只有lock()方法才会触发看门狗,其他的方法不会触发看门狗功能,
如lock(long, TimeUnit),tryLock(),
本文从结果触发,先理论分析,然后,代码实践验证。
先从源码讲解Redisson相关的知识,包括如何新建锁、释放锁、看门狗功能,
然后代码实战,验证这些功能,
感兴趣的同学可以手动调试。
帮助读者轻松应对知识交流与考核。

版本信息:
SpringBoot:2.1.7
Redisson:3.7.5

2 Redisson属性源码分析与验证

2.1 新建锁

位置:org.redisson.RedissonLock#tryLockInnerAsync
新建锁和可重入源码如下图所示,由源码可知,
Redisson使用Lua脚本新建锁。

  • 为什么?
    保证操作原子性。
    因为新建数据和添加过期时间是两步操作,不能保证原子性,
    因此使用Lua实现,保证新建锁时是原子操作。

Redisson实现的锁是可重入的。同一个线程获取锁,无需重新排队申请,只需增加锁的持有次数。

  • 怎么做?
    判断锁是的属性是否存在。
    通过hexists判断锁的属性是否存在(UUID:threadId),如果存在,值+1,同时,更新锁过期时间,
    使用Lua脚本,保证原子性。

在这里插入图片描述

Lua脚本中的参数KEYS[1],ARGV[1], ARGV[2],

  • 分别对应哪些参数呢?
    KEYS[1]:Collections.singletonList(getName()),即锁的键名;
    ARGV[1]:internalLockLeaseTime,即锁过期时间;
    ARGV[2]:getLockName(threadId),即锁(Hash)属性的键,UUDI:threadId。
    其中,getLockName源码如下图所示,由图可知,UUID拼接threadId作为锁属性的键。
    顺便补充一下,Hash结构,键名,即获取该Hash的键,属性键名,即Hash内部数据的键。

在这里插入图片描述

下面看一下新建锁的调试过程,如下图所示,
(单步调试,如果操作不及时或者有其他时延,会导致Redis数据过期,因此,选择单步调试)
由图可知,新建的锁,键名称为redisLockKey,
默认锁过期时间为30秒,UUID为edb0f8cb-ec11-4b29-a107-12be152c4a5e,
threadId为71,锁属性的键为:edb0f8cb-ec11-4b29-a107-12be152c4a5e:71,属性键的值为1。
在这里插入图片描述
新建锁之后,会在Redis生成对应的数据,
如下图所示,由图可知,Redisson新建的锁键为redisLockKey,
锁属性名称为:edb0f8cb-ec11-4b29-a107-12be152c4a5e:71,属性值为1。
过期时间TTL已经开始倒计时了,实时的值为17秒。
在这里插入图片描述

2.2 获取锁

Redisson获取锁有两种方式:lock获取和tryLock获取。
lock()获取锁,如果得不到,会一直等,同时,lock()会开启看门狗功能,在看门狗巡查期间,锁不会过期;
lock(long, java.util.concurrent.TimeUnit)获取锁,会一直等,当锁过期后,即可自动获取;
tryLock()获取锁,得不到会一直等,当锁过期后,可自动获取到;
tryLock(long, long, java.util.concurrent.TimeUnit)获取锁,有两个时间参数,可以同时指定等待锁的最大时间,以及锁过期时间,如果等待最大时间大于锁过期时间,则锁被提前释放,重新生成锁;

2.2.1 lock

位置:java.util.concurrent.locks.Lock#lock

无参的lock方法来自JUC接口,
Redisson自身实现lock,并加入看门狗功能。
无返回值。
在这里插入图片描述
Redisson的实现源码如下图所示,这里给出路径,感兴趣可自行查看。
位置:org.redisson.RedissonLock#lock()

在这里插入图片描述

有参的lock源码如下图所示,
这个lock来自Redisson的RLock,添加了锁过期时间,
可以自定义锁的过期时间,这个获取锁的方法不会触发看门狗。
无返回值。

位置:org.redisson.api.RLock#lock
在这里插入图片描述
Redisson实现源码如下,这里给出路径,感兴趣可自行查看。
位置:org.redisson.RedissonLock#lock(long, java.util.concurrent.TimeUnit)
在这里插入图片描述

2.2.2 tryLock

位置:java.util.concurrent.locks.Lock#tryLock()
获取锁,无法获取时等待,当锁自动过期后,可自动获取。
有返回值,可通过返回值判断是否获得锁。
true:成功获取锁;
false:未获取锁。
在这里插入图片描述
Redisson实现tryLock源码如下图所示。
位置:org.redisson.RedissonLock#tryLock()
在这里插入图片描述

有参数的tryLock源码下图所示,有源码可知,
可指定最大等待锁时间、锁过期时间,
如果等待锁的最大时间小于锁过期时间,返回false,标识未得到锁。
如果等待锁的最大时间大于锁过期时间,等锁释放后,可自动获取锁,返回true,标识得到锁。
有返回值,可通过返回值判断是否获得锁。
true:成功获取锁;
false:未获取锁。
位置:org.redisson.api.RLock#tryLock

在这里插入图片描述

获取锁的实现源码如下图所示,这里给出路径,感兴趣可自行查看。
位置:org.redisson.RedissonLock#tryLock(long, long, java.util.concurrent.TimeUnit)
在这里插入图片描述

2.3 释放锁

位置:org.redisson.RedissonLock#unlockInnerAsync
释放锁源码如下图所示,由图可知,释放锁时会将锁属性的值-1,(加-1),
当释放的锁存在时,会将锁属性的值减1,减后的值大于零,重置锁的过期时间,保证锁的过期时间周期,
如果减1之后,锁属性值为0,即没有线程再持有锁,则删除该锁(del)。
在这里插入图片描述

2.4 锁续期

看门狗
位置:org.redisson.config.Config#lockWatchdogTimeout
看门狗的默认超时时间为30秒,即锁过期时间的续期为30秒,
看门狗的巡查周期为10秒,即每10秒更新一次锁的过期时间,设置为30秒。
两个是独立的时间系统。
30秒:锁的过期时间;
10秒:看门狗巡查周期。
这个10秒是如何得到的:30/3=10。

在这里插入图片描述

位置:org.redisson.RedissonLock#scheduleExpirationRenewal
看门狗进行锁续期的源码如下图所示,由源码可知,
当锁存在时,会延迟10秒执行更新锁过期时间,过期时间为30秒,
看门狗的巡查周期就是这个延迟时间:10秒。
在这里插入图片描述
当然,好奇的同学会问,锁的默认过期时间怎么就是30秒,
这个30秒是从哪里来的呢?
过期时间(internalLockLeaseTime)默认值取自看门狗的超时时间:30秒,上文有看门狗的默认值源码。
在这里插入图片描述

3 实战

版本信息:
SpringBoot:2.1.7
Redisson:3.7.5

3.1 依赖

<!--Redisson-->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.7.5</version>
</dependency>

3.2 配置

3.2.1 Redis配置

application-dev.yml

spring:profiles: devredis:host: localhostport: 6379# 连接超时时间:毫秒timeout: 60000database: 0lettuce:pool:# 连接池最大连接数量max-active: 8# 连接池最大阻塞等待时间:-1无限制max-wait: 60000# 连接池最大空闲连接数量max-idle: 8# 连接池最小空闲连接数量min-idle: 0

3.2.2 绑定Redis配置

package com.hardsoft.monkeyrun.common.lock;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;/*** Redis连接参数.** @author xindaqi* @since 2021/4/2 11:41*/
@Configuration
@ConfigurationProperties(prefix="spring.redis")
public class RedisConnectionParams {/*** 主机名*/private String host;/*** 主机端口*/private String port;/*** Redis连接超时时间:毫秒*/private int timeout;/*** Redis数据库编号*/private int database;public void setHost(String host) {this.host = host;}public String getHost() {return host;}public void setPort(String port) {this.port = port;}public String getPort() {return port;}public void setTimeout(int timeout) {this.timeout = timeout;}public int getTimeout() {return timeout;}public void setDatabase(int database) {this.database = database;}public int getDatabase() {return database;}@Overridepublic String toString() {return "RedisConnectionParams{" +"host='" + host + '\'' +", port='" + port + '\'' +", timeout=" + timeout +", database=" + database +'}';}
}

3.2.3 Redisson配置

package com.hardsoft.monkeyrun.common.config;import com.hardsoft.monkeyrun.common.lock.RedisConnectionParams;
import com.hardsoft.monkeyrun.common.lock.RedissonLocker;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;import static com.hardsoft.monkeyrun.common.constrant.StringConstant.COLON;
import static com.hardsoft.monkeyrun.common.constrant.StringConstant.REDISSON_SCHEMA;/*** Redisson配置.** @author xindaqi* @since 2021/4/2 11:34*/
@Configuration
public class RedissonConfig {@ResourceRedisConnectionParams redisConnectionParams;/*** 单机模式RedissonClient** @return*/@Bean(destroyMethod = "shutdown")public RedissonClient redissonClient() {Config config = new Config();SingleServerConfig singleServerConfig = config.useSingleServer();StringBuilder redisAddress = new StringBuilder();redisAddress.append(REDISSON_SCHEMA).append(redisConnectionParams.getHost()).append(COLON).append(redisConnectionParams.getPort());singleServerConfig.setAddress(redisAddress.toString());return Redisson.create(config);}@Beanpublic RedissonLocker redissonLocker(RedissonClient redissonClient) {return new RedissonLocker(redissonClient);}}

3.2.4 Redisson获取锁封装

package com.hardsoft.monkeyrun.common.lock;import com.hardsoft.monkeyrun.common.constrant.BooleanConstant;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;/*** Redisson锁** @author xindaqi* @since 2021/3/30 19:01*/
@Component
public class RedissonLocker {RedissonClient redissonClient;public RedissonLocker(RedissonClient redissonClient) {this.redissonClient = redissonClient;}/*** 获取锁* 如果锁不可用,则当前线程处于休眠状态,直到获得锁为止** @param lockKey 锁键*/public void lock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);lock.lock();}/*** 获取锁* 如果锁不可用,则当前线程处于休眠状态,直到获得锁为止,* 如果获取到锁后,执行结束后解锁或达到超时时间后自动释放锁** @param lockKey 锁键* @param timeout 超时时间*/public void lock(String lockKey, int timeout) {RLock lock = redissonClient.getLock(lockKey);lock.lock(timeout, TimeUnit.SECONDS);}/*** 获得锁* 如果锁不可用,则当前线程处于休眠状态,直到获得锁后,* 执行结束后解锁或达到超时时间后会自动释放锁** @param lockKey 锁键* @param unit 时间单位* @param timeout 超时时间*/public void lock(String lockKey, TimeUnit unit, int timeout) {RLock lock = redissonClient.getLock(lockKey);lock.lock(timeout, unit);}/*** 释放锁** @param lockKey 锁键*/public void unlock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);lock.unlock();}/*** 尝试获取锁* 获取到立即返回True* 未获取到返回False** @param lockKey 锁键* @return 获取锁标志位,True:成功获取锁,False:未获取锁*/public boolean tryLock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);return lock.tryLock();}/*** 尝试获取锁* 在等待时间内获取到锁则返回True,否则返回False* 获取到锁,两种执行逻辑:* 1.执行之后,释放锁* 2.达到释放时间leaseTime后,释放锁** @param lockKey 锁键* @param waitTime 等待时间* @param leaseTime 释放时间* @param unit 时间单位* @return 获取锁标志位,True:成功获取锁,False:未获取锁* @throws InterruptedException*/public boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {RLock lock = redissonClient.getLock(lockKey);return lock.tryLock(waitTime, leaseTime, unit);}/*** 判断锁是否被任意一个线程持有** @param lockKey 锁键* @return 锁持有标志位,True:锁被线程持有,False:锁没有被线程持有*/public boolean isLocked(String lockKey) {RLock lock = redissonClient.getLock(lockKey);return lock.isLocked();}public void setRedissonClient(RedissonClient redissonClient) {this.redissonClient = redissonClient;}
}

3.3 接口测试

package com.hardsoft.monkeyrun.api.facade.lock;import com.hardsoft.monkeyrun.common.constrant.BooleanConstant;
import com.hardsoft.monkeyrun.common.constrant.DigitalConstant;
import com.hardsoft.monkeyrun.common.enums.BizExceptionCodeEnums;
import com.hardsoft.monkeyrun.common.exception.BizException;
import com.hardsoft.monkeyrun.common.lock.RedissonLocker;
import com.hardsoft.monkeyrun.common.response.Response;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.concurrent.TimeUnit;import static com.hardsoft.monkeyrun.common.constrant.StringConstant.REDIS_LOCK_KEY;/*** 锁测试** @author xindaqi* @since 2021-04-03 11:28:18*/
@RestController
@RequestMapping("/v1/lock")
public class LockTest {private static final Logger logger = LoggerFactory.getLogger(LockTest.class);private final String STOCK_WITH_REDISSON = "stock_with_redisson";private final String STOCK_WITHOUT_REDISSON = "stock_without_redisson";@ResourceRedissonLocker redissonLocker;/*** 测试Redisson锁tryLock,不会激活看门狗,锁过期后,直接被清除* * @return 响应*/@GetMapping("/redisson/try-lock/test")public Response<String> testRedissonTryLock() {try {boolean lock1 = redissonLocker.tryLock(REDIS_LOCK_KEY, 100, 10, TimeUnit.SECONDS);if (lock1) {logger.info(">>>>>>>>得到锁");Thread.sleep(70000);return Response.success();} else {logger.info(">>>>>>>>未得到锁");return Response.fail();}} catch(Exception ex) {logger.error(">>>>>>>>Redisson异常:", ex);throw new BizException(BizExceptionCodeEnums.FAIL);} finally {logger.info(">>>>>>>释放Redisson锁");redissonLocker.unlock(REDIS_LOCK_KEY);}}/*** 测试Redisson看门狗功能:使用lock()获取锁,激活看门狗功能,每10秒自动续期30秒过期时间。* * @return 响应*/@GetMapping("/redisson/lock/test")public Response<String> testRedissonLock() {try {redissonLocker.lock(REDIS_LOCK_KEY);Thread.sleep(70000);return Response.success();} catch(Exception ex) {logger.error(">>>>>>>>Redisson异常:", ex);throw new BizException(BizExceptionCodeEnums.FAIL);} finally {logger.info(">>>>>>>释放Redisson锁");redissonLocker.unlock(REDIS_LOCK_KEY);}}
}

Redis结果:

在这里插入图片描述

4 结论

(1)Redisson锁存储使用Hash,Hash键为锁的键名,属性的键为UUID拼接ThreadId,格式:UUID:ThreadID,如xxx-xxx:30,属性的值为同一个线程获取锁的次数,Redisson的锁是可重入的,同一个线程获取锁,锁的数量+1,释放锁,数量减1;
(2)Redisson获取锁有两种方式,lock获取和tryLock获取,其中:
(2.1)lock获取锁时,如果第一次尝试获取获取失败,会一直等待,直到获取锁。Redisson的lock获取锁时,会为锁配置过期时间,当锁超过过期时间后,会自动释放,避免死锁,可以重新获得锁;
(2.2)lock()原始方法获取(不自定义超时(过期)时间)会激活看门狗。即通过lock()方法成功获取锁后,逻辑执行的时间超过看门狗超时时间:10秒,会自动为锁续期(增加)10秒的过期时间,使锁的过期时间保持在30秒(默认的锁过期时间);
(2.3)lock(long, TimeUnit)带参方法可以指定锁过期时间,但是,不会触发看门狗自动续期;锁过期后,自动释放锁;
(2.4)tryLock方法返回标识位,获取成功,返回true,获取失败,返回false;tryLock尝试获取锁,无法获取锁时会等待,同时,tryLock有最大的等待时间,如果获取锁的等待时间超过最大等待时间,会放弃获取锁,并返回false;tryLock不会触发看门狗,无法自动为锁续期;
(2.5)tryLock可以同时指定等待锁的最大时间,以及锁过期时间,如果等待最大时间大于锁过期时间,则锁被提前释放,重新生成锁;
(3)Redisson看门狗默认巡查周期为10秒,锁续期时间(过期时间)30秒;
(4)Redisson看门口生效的方法为:lock(),其他方法均不会触发看门狗。

相关文章:

简洁易懂:源码+实战讲解Redisson并发锁及看门狗自动续期

1 缘起 有一次同事问Redisson存储的键是否为hash&#xff1f; 我当时&#xff0c;没有看Redisson的相关源码&#xff0c;只知道应用&#xff0c; 所以没有办法回答&#xff0c;于是开始看看Redisson实现的源码&#xff0c; 顺便写了一个单机Redisson测试&#xff0c; 发现Redi…...

TCP 三次握手和四次挥手

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录TCP 建立连接(三次握手)为啥不能是 4 次&#xff1f;为啥不能是 2 次&#xff1f;三次握手的意义&#xff1a;TCP 断开连接(四…...

JavaWeb复习

JavaWeb复习一.概述1.概念2.B/S和C/S 架构二.HTTP通信协议概述1.概念2.HTTP1.0 与 HTTP1.1 版本3.HTTP 协议组成4.常见状态码5.GET 与 POST 请求方式三.Tomcat1.Web服务器介绍2.安装&#xff08;Windows&#xff09;3.Tomcat目录结构4.server.xml部分配置解释四.Servlet1.概念2…...

P14 PyTorch AutoGrad

前言&#xff1a;激活函数与loss的梯度PyTorch 提供了Auto Grad 功能&#xff0c;这里系统讲解一下torch.autograd.grad系统的工作原理&#xff0c;了解graph 结构目录&#xff1a;1: require_grad False2: require_grad True3&#xff1a; 多层bakcward 原理4&#xff1a; in…...

前端报表如何实现无预览打印解决方案或静默打印

在前端开发中&#xff0c;除了将数据呈现后&#xff0c;我们往往需要为用户提供&#xff0c;打印&#xff0c;导出等能力&#xff0c;导出是为了存档或是二次分析&#xff0c;而打印则因为很多单据需要打印出来作为主要的单据来进行下一环节的票据支撑&#xff0c; 而前端打印可…...

Operating System Course 2 - My OS

Computer Startup process上一篇&#xff1a;http://t.csdn.cn/XfUKt 讲到这个启动设备的第一个扇区&#xff1a;引导扇区。那么引导扇区的代码长什么样子&#xff1f;这里得看引导扇区代码源文件bootsect.s&#xff08;.s后缀文件为用汇编语言编写的源代码文件&#xff09;。另…...

离散数学 课时一 命题逻辑的基本概念

1 命题 1、命题&#xff1a;可以判断其真值的陈述句 2、真值&#xff1a;真或者假(1或者0) 3、真命题&#xff1a;真值为真的命题 4、假命题&#xff1a;真值为假的命题 5、原子命题&#xff1a;不可以再被分解成更简单的命题 6、复合命题&#xff1a;由原子命题通过联结词联结…...

Word文档带有权限密码怎么办?

Word文档的权限密码指的是什么&#xff1f;其实这是Word文档的保护方法之一&#xff0c;具体指Word文档的编辑、修改受到了限制&#xff0c;需要输入密码才能进行。 设置了权限密码的Word文档还是可以直接打开&#xff0c;只有当需要编辑或者修改内容的时候&#xff0c;才会发…...

C++多态

1. 多态的概念1.1 概念多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态举个例子&#xff1a;比如买票这个行为&#xff0c;当普通人买票时&#xff0c;是全价买票&#xff1b…...

访问学者如何申请美国J1签证?

一、申请美国J1签证的步骤&#xff1a; 第一步&#xff1a;填写I901表。 填写I901表会收取SERVIS费用180美元&#xff0c;可以用VISA/Master卡直接网上支付。填完后打印收据单或者存成PDF后续再打印&#xff0c;记下I901收据编号。 第二步&#xff1a;DS-160表填写。 填写DS-…...

使用gitlab ci/cd来发布一个.net 项目

gitlab runner的安装和基本使用:https://bear-coding.blog.csdn.net/article/details/120591711安装并给项目配置完gitlab runner后再操作后面步骤。实现目标&#xff1a;master分支代码有变更的时候自动构建build。当开发人员在gitlab上给项目打一个tag标签分支的时候自动触发…...

笔试题-2023-蔚来-数字芯片设计【纯净题目版】

回到首页:2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 推荐内容:数字IC设计学习比较实用的资料推荐 题目背景 笔试时间:2022.08.24应聘岗位:校招-芯片逻辑综合工程师-智能硬件笔试时长:90min笔试平台:nowcoder牛客网题目类型:不定项选择题(15道)、填空题…...

ThreadLocal 详解

ThreadLocal简介JDK源码对ThreadLocal类的注释如下&#xff1a;ThreadLocal提供线程局部变量&#xff0c;使得每个线程都有自己的、独立初始化的变量副本ThreadLocal实例通常是类中的private static字段&#xff0c;用于将状态与线程相关联&#xff0c;如用户ID、事务ID只要线程…...

【Java 面试合集】重写以及重载有什么区别能简单说说嘛

重写以及重载有什么区别能简单说说嘛 前述 这是一道非常基础的面试题&#xff0c;我们在回答的过程中一定要逐一横向比较。 从方法的 修饰符&#xff0c;返回值&#xff0c;方法名&#xff0c;含义&#xff0c;参数等方面进行逐一分析来比较不同。 话不多话&#xff0c;看下…...

到底什么是股票委托接口?

在量化股票市场上&#xff0c;常见的股票委托接口其实有着不一样的交集&#xff0c;就拿股票交易接口&#xff0c;在量化股票跟程序化交易中&#xff0c;有共同之处就是在于直接委托执行下单&#xff0c;并且能很快的就能够将策略输出在账户持仓数据中&#xff0c;继续缓存下来…...

Linux驱动:VPU

1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. 概述 VPU 是用来进行图像、视频数据进行硬件编、解码的硬件模块。内部集成了 Encoder、Decoder 功能部件进行图像、视频数据进行硬件编、解码&a…...

简介Servlet

目录 一、maven中心库 二、简介Servlet 三、实现Servlet动态页面 1、创建一个maven项目 2、引入依赖 3、创建目录结构 4、编写Servlet代码 5、打包 6、部署 7、验证程序 四、Servlet的运行原理 五、Tomcat伪代码 1、Tomcat初始化 a、让Tomcat先从指定的目录…...

Learning C++ No.7

引言&#xff1a; 北京时间&#xff1a;20223/2/9/22:20&#xff0c;距离大一下学期开学还有2天&#xff0c;昨天收到好消息&#xff0c;开学不要考试了&#xff0c;我并不是害怕考试&#xff0c;考试在我心里&#xff0c;地位不高&#xff0c;可能只有当我挂了&#xff0c;才能…...

【MyBatis】第八篇:一级,二级缓存

其实缓存字面的意思就是将一些内容缓存下来&#xff0c;等下次使用的时候可以直接调用&#xff0c;通过数据库得到数据&#xff0c;有时候会使用相同的数据&#xff0c;所以mybatis自然也支持缓存。 而mybatis按照缓存的效果可以分两大类&#xff1a;一级缓存和二级缓存。 一…...

【大唐杯备考】——5G基站开通与调测(学习笔记)

&#x1f4d6; 前言&#xff1a;本期介绍5G基站开通与调测。 目录&#x1f552; 1. 概述&#x1f552; 2. 5G基站开通与调测基础&#x1f558; 2.1 3.5GHz单模100MHz配置&#xff08;S111&#xff09;&#x1f558; 2.2 3.5GHz单模100MHz配置&#xff08;S111111&#xff09;&a…...

redhat7 忘记root密码,重置办法

来自https://www.tracymc.cn/archives/802 亲测可用&#xff0c;太感谢了&#xff0c;在此记录一下&#xff0c;原文有图 1.启动的时候,在有启动项界面,相应启动项内核名称上按“e”; 2.进入后,找到linux16开头的地方,按“end”键或者controle到最后,输入rd.break,再按ctrlx进…...

QML- 对象属性

QML- 对象属性一、概述二、id 属性三、Property 属性1. 定义属性1. 自定义属性定义中的有效类型2. 为属性属性赋值1. 初始化时的值赋值2. 命令式赋值3. 静态值和绑定表达式值4. 类型安全5. 特殊属性类型1. 对象列表属性2. 分组属性6. 属性别名1. 属性别名的注意事项2. 属性别名…...

将.js文件转成vue标签结构的样式

例如&#xff1a;下图所示&#xff1a; 依次识别获取.js文件中的tag和props&#xff0c;可以理解为字符串拼接&#xff0c;将整个vue的标签结构看作是一个字符串。 话不多说&#xff0c;先放上完整代码&#xff0c;思路看代码备注。&#xff08;自己实现的时候&#xff0c;可以…...

前端知识点复盘

组件和jsx <body><div id"root"></div><script type"text/babel">const root ReactDOM.createRoot(document.getElementById("root"))class App extends React.Component {render() {return (<div> <h1>s…...

前端JavaScript获取图片文件的真实格式

常见方式判断图片格式 当我们进行前端开发&#xff0c;需要处理图片上传功能&#xff0c;针对图片格式做判断时&#xff0c;常规的方法都是使用文件后缀名来判断&#xff0c;如下代码所示&#xff1a; input.addEventListener(change, (e) > {const file e.target.files[…...

今天面了一个来华为要求月薪25K,明显感觉他背了很多面试题...

最近有朋友去华为面试&#xff0c;面试前后进行了20天左右&#xff0c;包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试。 据他所说&#xff0c;80%的人都会栽在第一轮面试&#xff0c;要不是他面试前做足准备&#xff0c;估计都坚持不完后面几轮面试。 其实&…...

11 Advanced CNN

文章目录GoogLeNetInception Module1x1 Conv计算效果代码实现总结ResNet (残差网络)问题引入梯度消失与传统神经网络的比较代码实现课程来源&#xff1a; 链接对于前篇中所提到问题&#xff0c;设计出的是一种类似于LeNet5的线性结构&#xff0c;而对于大多数问题&#xff0c;简…...

亿级高并发电商项目---万达商城项目搭建(二)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…...

UML术语标准和分类

一、UML术语标准 1&#xff0e;中文UML术语标准 中国软件行业协会&#xff08;CSIA&#xff09;与日本UML建模推进协会&#xff08;UMTP&#xff09;共同在中国推动的UML专家认证&#xff0c;两个协会共同颁发认证证书、两国互认&#xff0c;CSIA与UMTP共同推出了UML中文术语…...

LeetCode 刷题系列 -- 151. 反转字符串中的单词

给你一个字符串 s &#xff0c;请你反转字符串中 单词 的顺序。单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。注意&#xff1a;输入字符串 s中可能会存在前导空格、尾随空格或…...

吉林有做网站的吗/能打开各种网站的搜索引擎

SSM工作原理 SSM&#xff08;SpringSpringMVCMyBatis&#xff09;框架集由Spring、MyBatis两个开源框架整合而成&#xff08;SpringMVC是Spring中的部分内容&#xff09;。常作为数据源较简单的web项目的框架。 Spring Spring就像是整个项目中装配bean的大工厂&#xff0c;在…...

wordpress 培训插件/网站排名优化方法

1. 编写程序&#xff0c;声明一个method方法&#xff0c;在方法中打印一个108 的型矩形&#xff0c; 在main方法中调用该方法。 2. 修改上一个程序&#xff0c;在method方法中&#xff0c;除打印一个108的型矩形外&#xff0c;再 计算该矩形的面积&#xff0c;并将其作为方法返…...

阿里巴巴1688怎么做网站/长沙网站seo哪家公司好

1 网络IO模型 memcached是多线程&#xff0c;非阻塞IO复用的网络模型&#xff0c;分为监听主线程和worker子线程&#xff0c;监听线程监听网络连接&#xff0c;接受请求后&#xff0c;将连接描述字pipe传递给worker线程&#xff0c;进行读写IO&#xff0c;网络层使用libevent封…...

新乡商城网站建设价格/营销广告语

1.中文乱码 很多同学都安装了Android Studio&#xff0c;但是发现中文是乱码&#xff0c;其实这个很好解决的。在IDE里点击File&#xff0c;选择Settings...快捷键是Ctrlalts 在打开的窗口中&#xff0c;找到IDE Settings下的Appearance&#xff0c;在右侧勾选上“Override def…...

山东网站排行/个人网站推广

package com.tiger;import java.nio.ByteBuffer;/*** 缓冲区分片案例* 在现有缓冲区对象中创建一个子缓冲区&#xff0c;即在现有缓冲区上切出一片作为一个新的缓冲区&#xff0c;* 但现有缓冲区与创建的子缓冲区在底层数组层面上是数据共享&#xff1b;* 也就是说&#xff0c;…...

佛山做网站建设公司/职业技能培训学校

昨晚与闺中密友聚餐&#xff0c;话题自然少不了女人世界的种种。不知怎么&#xff0c;话题就扯到了女人的头发上。从头发谈到染发又谈到染发剂&#xff0c;又从染发剂谈到经常染发有致癌的可能。一位朋友刚换了一种染发剂的颜色&#xff0c;浅棕色里掺杂着丝丝灰白&#xff0c;…...