ZooKeeper 实战(五) Curator实现分布式锁
文章目录
- ZooKeeper 实战(五) Curator实现分布式锁
- 1.简介
- 1.1.分布式锁概念
- 1.2.Curator 分布式锁的实现方式
- 1.3.分布式锁接口
- 2.准备工作
- 3.分布式可重入锁
- 3.1.锁对象
- 3.2.非重入式抢占锁
- 测试代码
- 输出日志
- 3.3.重入式抢占锁
- 测试代码
- 输出日志
- 4.分布式非可重入锁
- 4.1.锁对象
- 4.2.重入式抢占锁
- 测试代码
- 输出日志
- 5.分布式可重入读写锁
- 5.1.锁对象
- 5.2.读锁和写锁的竞争
- 测试代码
- 输出日志
- 6.共享信号量
- 6.1.锁对象
- 6.2.信号量抢占
- 测试代码
- 输出日志
- 7.多共享锁
- 7.1.锁对象
- 7.2.获取共享锁
- 测试代码
- 输出日志
ZooKeeper 实战(五) Curator实现分布式锁
1.简介
1.1.分布式锁概念
分布式锁是一种用于实现分布式系统中的同步机制的技术。它允许在多个进程或线程之间实现互斥访问共享资源,以避免并发访问时的数据不一致问题。分布式锁的主要目的是在分布式系统中提供类似于全局锁的效果,以确保在任何时刻只有一个进程或线程可以访问特定的资源。
zookeeper基于临时有序节点实现分布式锁。每个客户端对某个临界资源加锁时,在zookeeper上的与该临界资源对应的指定节点的目录下,生成一个唯一的临时有序节点。 判断是否获取锁的方式很简单,只需要判断临时有序节点中序号最小的那个是否由自身创建。 当释放锁的时候,只需将这个临时有序节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。
1.2.Curator 分布式锁的实现方式
curator-recipes中实现的锁有五种:
- Shared Reentrant Lock 分布式可重入锁
- Shared Lock 分布式非可重入锁
- Shared Reentrant Read Write Lock 可重入读写锁
- Shared Semaphore 共享信号量
- Multi Shared Lock 多共享锁
1.3.分布式锁接口
org.apache.curator.framework.recipes.locks.InterProcessLock
该接口为分布式锁的行为规范,定义了获取锁和释放锁方法。
public interface InterProcessLock
{/*** 阻塞获取锁(如果未获取到锁,则一直阻塞)*/public void acquire() throws Exception;/*** 非阻塞获取锁** @param time 超时时间* @param unit 时间单位* @return 如果在超时时间内获取到锁则返回true,否则返回false* @throws Exception ZK errors, connection interruptions*/public boolean acquire(long time, TimeUnit unit) throws Exception;/*** 释放锁。每次获取锁之后必须调用该方法释放锁(acquire和release成对出现)*/public void release() throws Exception;/*** 判断该线程是否获取到锁* @return true/false*/boolean isAcquiredInThisProcess();
}
2.准备工作
首先,需要读者掌握 Ideal 同一项目启动多个Service的能力,详细教程可参考博主另一篇博客,博主创建了两个启动实例,一个端口号为8888,另一个9981,此处随意只要不与其他服务的端口号冲突即可。
另外,在启动项目之前,请根据先前所写的教程启动zookeeper的单机服务器 ,参考ZooKeeper 实战(一) 超详细的单机与集群部署教程,并创建一个存储数据节点:路径/ahao/data
,数据内容40
。(可参照如下操作,由于博主已经创建过,所以重新设置了一遍)
3.分布式可重入锁
可重入锁是一种自我保护的锁,允许同一进程或线程多次获得相同的锁,而不会造成死锁。
3.1.锁对象
类路径:org.apache.curator.framework.recipes.locks.InterProcessMutex
公开构造方法如下:
/*** @param client 当前客户端实例* @param path 锁节点路径*/public InterProcessMutex(CuratorFramework client, String path)/*** @param client 当前客户端实例* @param path 锁节点路径* @param driver 锁驱动实例(工具类,只要提供两个方法:#createsTheLock 创建锁节点,#getsTheLock 获取当前锁)*/public InterProcessMutex(CuratorFramework client, String path, LockInternalsDriver driver)
3.2.非重入式抢占锁
由于非重入式抢占锁的场景对于5种分布式锁实现方式均适用,并且测试场景均一样,所以本节介绍完非重入式抢占锁的测试场景之后,对应的另4种实现方式的该测试场景将不在赘述。
测试场景:有两台服务实例 C1,C2。C1和C2个有两个线程(线程池调度)并发执行,对同一共享资源(/ahao/data
中的数据)进行减1 操作。
测试代码
/*** @Name: CuratorDemoApplication* @Description:* @Author: ahao* @Date: 2024/1/10 3:29 PM*/
@Slf4j
@SpringBootApplication
public class CuratorDemoApplication implements ApplicationRunner{@Autowiredprivate CuratorFramework client;public static void main(String[] args) {SpringApplication.run(CuratorDemoApplication.class,args);}// 存储数据节点的路径final String dataPath = "/ahao/data";@Overridepublic void run(ApplicationArguments args) throws Exception {log.info("。。。。。。。。。。。。。容器初始化完毕。。。。。。。。。。。。。。");// 锁节点路径String lockPath = "/ahao/lock";TimeUnit.SECONDS.sleep(3);// 创建可重入锁InterProcessMutex mutex = new InterProcessMutex(client,lockPath);// 创建一个线程池ExecutorService executorService = Executors.newFixedThreadPool(2);// 保证并发执行,当前时间的秒针部分为0则结束循环int seconds = LocalDateTime.now().getSecond();while (seconds != 0){seconds = LocalDateTime.now().getSecond();}// 提交两个任务for (int i = 0; i < 2; i++) {executorService.submit(() -> {while (share(mutex)) {try {// 睡眠0.5秒TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});}}/*** 用来模拟临界资源的方法*/public boolean share(final InterProcessMutex mutex){boolean b = true;try {// 获取锁if (mutex.acquire(3, TimeUnit.SECONDS)) {// 获取数据节点中的值byte[] bytes = client.getData().forPath(dataPath);String s = new String(bytes);Integer integer = Integer.valueOf(s);if(integer > 0){// 设置新值client.setData().forPath(dataPath,String.valueOf(integer-1).getBytes(StandardCharsets.UTF_8));log.info("当前值:{}",integer);b = true;}else {log.info("任务已完成。。。。");b = false;}}} catch (Exception e) {throw new RuntimeException(e);} finally {try {// 释放锁mutex.release();return b;} catch (Exception e) {log.error("释放锁失败");throw new RuntimeException(e);}}}
}
输出日志
从下方日志可见,没有出现重复的数值,保证了分布式系统中实现互斥访问共享资源,避免并发访问时的数据不一致问题。
实例c1:
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:40
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:39
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:36
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:35
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:32
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:31
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:28
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:27
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:24
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:23
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:20
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:19
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:16
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:15
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:12
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:11
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:8
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:7
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:4
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:3
2024-01-15 INFO 65348 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 任务已完成。。。。
2024-01-15 INFO 65348 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 任务已完成。。。。
实例c2:
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:38
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:37
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:34
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:33
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:30
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:29
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:26
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:25
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:22
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:21
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:18
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:17
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:14
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:13
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:10
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:9
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:6
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:5
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 当前值:2
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 当前值:1
2024-01-15 INFO 65387 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 任务已完成。。。。
2024-01-15 INFO 65387 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 任务已完成。。。。
3.3.重入式抢占锁
测试场景:有两台服务实例 C1,C2。C1和C2个有两个线程(线程池调度)并发执行,对同一共享资源(/ahao/data
中的数据)进行连续两次的减1 操作,并且第一次操作成功后才能进行第二次操作。
测试代码
具体的减1操作抽离成方法,以便接下来的测试。
@Overridepublic void run(ApplicationArguments args) throws Exception {log.info("。。。。。。。。。。。。。容器初始化完毕。。。。。。。。。。。。。。");String lockPath = "/ahao/lock";TimeUnit.SECONDS.sleep(3);// 创建可重入锁InterProcessMutex mutex = new InterProcessMutex(client,lockPath);// 创建一个线程池ExecutorService executorService = Executors.newFixedThreadPool(2);// 保证并发执行,当前时间的秒针部分为0则结束循环int seconds = LocalDateTime.now().getSecond();while (seconds != 0){seconds = LocalDateTime.now().getSecond();}// 提交两个任务for (int i = 0; i < 2; i++) {executorService.submit(() -> {// 循环执行while (share(mutex,1)) {try {// 睡眠0.5秒TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});}}/*** 用来模拟临界资源的方法*/public boolean share(final InterProcessLock mutex, int n){boolean b = true;try {// 获取锁if (mutex.acquire(3, TimeUnit.SECONDS)) {// 减1操作b = doLock(n);// 最多执行三次if (b && n < 3){b = share(mutex,n+1);}}} catch (Exception e) {throw new RuntimeException(e);} finally {try {mutex.release();return b;} catch (Exception e) {log.error("释放锁失败");throw new RuntimeException(e);}}}// 减1操作private boolean doLock(int n) throws Exception {// 获取数据节点中的值byte[] bytes = client.getData().forPath(dataPath);String s = new String(bytes);Integer integer = Integer.valueOf(s);// 判断是否为0if(integer > 0){// 设置新值client.setData().forPath(dataPath,String.valueOf(integer-1).getBytes(StandardCharsets.UTF_8));log.info("第{}次加锁当前值:{}",n,integer);return true;}else {log.info("任务已完成。。。。");return false;}}
输出日志
由此可见,通过日志可以发现,每次获取到锁的线程都是连续执行三次并且重复获取锁并释放。
实例c1:
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:34
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:33
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:32
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:31
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:30
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:29
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:22
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:21
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:20
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:19
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:18
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:17
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:10
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:9
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:8
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:7
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:6
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:5
2024-01-16 INFO 91120 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 任务已完成。。。。
2024-01-16 INFO 91120 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 任务已完成。。。。
实例c2:
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:40
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:39
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:38
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:37
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:36
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:35
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:28
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:27
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:26
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:25
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:24
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:23
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:16
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:15
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:14
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:13
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:12
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:11
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:4
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第2次加锁当前值:3
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 第3次加锁当前值:2
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 第1次加锁当前值:1
2024-01-16 INFO 91115 --- [pool-3-thread-2] com.ahao.demo.CuratorDemoApplication : 任务已完成。。。。
2024-01-16 INFO 91115 --- [pool-3-thread-1] com.ahao.demo.CuratorDemoApplication : 任务已完成。。。。
4.分布式非可重入锁
与org.apache.curator.framework.recipes.locks.InterProcessMutex
类似,只是不可重入。
4.1.锁对象
类路径:InterProcessSemaphoreMutex
公开构造方法如下:
/*** @param client 当前客户端实例* @param path 锁节点路径*/ public InterProcessSemaphoreMutex(CuratorFramework client, String path);
4.2.重入式抢占锁
测试场景:有一台服务实例 C1。C1中的main线程进行连续两次的加锁操作。
测试代码
/*** @Name: CuratorDemoApplication* @Description:* @Author: ahao* @Date: 2024/1/10 3:29 PM*/
@Slf4j
@SpringBootApplication
public class CuratorDemoApplication implements ApplicationRunner{@Autowiredprivate CuratorFramework client;public static void main(String[] args) {SpringApplication.run(CuratorDemoApplication.class,args);}String dataPath = "/ahao/data";@Overridepublic void run(ApplicationArguments args) throws Exception {log.info("。。。。。。。。。。。。。容器初始化完毕。。。。。。。。。。。。。。");String lockPath = "/ahao/lock";TimeUnit.SECONDS.sleep(3);// 创建非可重入锁InterProcessSemaphoreMutex mutex = new InterProcessSemaphoreMutex(client, lockPath);// 调用测试方法share(mutex);}/*** 用来模拟临界资源的方法*/public void share(final InterProcessLock mutex){try {// 获取锁if (mutex.acquire(5, TimeUnit.SECONDS)) {log.info("第一次加锁成功");}// 再次获取锁if (!mutex.acquire(5, TimeUnit.SECONDS)) {log.info("第二次加锁失败了");}} catch (Exception e) {throw new RuntimeException(e);} finally {try {// 获取多少次锁就要释放多少次mutex.release();mutex.release();} catch (Exception e) {log.error("释放锁失败:{}",e.getStackTrace());throw new RuntimeException(e);}}}}
输出日志
本次测试仅需要启动一个实例c1即可,用于测试分布式非可重入锁多次加锁的场景。根据以下输出日志可知,第一次加锁成功,在第二次加锁时超时失败了,导致之后在第二次释放锁时出现异常。
2024-01-16 INFO 45025 --- [ main] com.ahao.demo.CuratorDemoApplication : 第一次加锁成功
2024-01-16 INFO 45025 --- [ main] com.ahao.demo.CuratorDemoApplication : 第二次加锁失败了
2024-01-16 ERROR 45025 --- [ main] com.ahao.demo.CuratorDemoApplication : 释放锁失败:org.apache.curator.shaded.com.google.common.base.Preconditions.checkState(Preconditions.java:444)
2024-01-16 INFO 45025 --- [ main] ConditionEvaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2024-01-16 ERROR 45025 --- [ main] o.s.boot.SpringApplication : Application run failedjava.lang.IllegalStateException: Failed to execute ApplicationRunnerat org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762)at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:749)at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292)at com.ahao.demo.CuratorDemoApplication.main(CuratorDemoApplication.java:41)
Caused by: java.lang.RuntimeException: java.lang.IllegalStateException: Not acquiredat com.ahao.demo.CuratorDemoApplication.share(CuratorDemoApplication.java:83)at com.ahao.demo.CuratorDemoApplication.run(CuratorDemoApplication.java:56)at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:759)... 5 common frames omitted
Caused by: java.lang.IllegalStateException: Not acquiredat org.apache.curator.shaded.com.google.common.base.Preconditions.checkState(Preconditions.java:444)at org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex.release(InterProcessSemaphoreMutex.java:68)at com.ahao.demo.CuratorDemoApplication.share(CuratorDemoApplication.java:80)... 7 common frames omitted
5.分布式可重入读写锁
读写锁顾名思义,包含两把锁:读锁和写锁。当写锁未生效(未被获取)时,读锁能够被多个线程获取使用。但是写锁只能被一个线程获取持有。 只有当写锁释放时,读锁才能被持有。可重入表示一个拥有写锁的线程可重入读锁,但是读锁却不能进入写锁,读锁可以重入读锁。 这也意味着写锁可以降级成读锁, 比如请求写锁 —>读锁 —->释放写锁。 从读锁升级成写锁是不行的。可重入读写锁是“公平的”,每个实例将按请求的顺序获取锁。
5.1.锁对象
类路径:org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock
公开构造方法如下:
/*** @param client 当前客户端实例* @param path 锁节点路径*/ public InterProcessReadWriteLock(CuratorFramework client, String basePath)/*** @param client 当前客户端实例* @param path 锁节点路径* @param lockData 存储在锁节点的数据内容*/public InterProcessReadWriteLock(CuratorFramework client, String basePath, byte[] lockData)
5.2.读锁和写锁的竞争
测试场景:有两台服务实例 C1,C2。C1和C2个有3个线程(2个读线程和1个写线程)并发执行,其中写线程进行加1操作并重复执行5次,每次都加写锁,而读线程进行查询操作并重复执行10次每次都加读锁。
测试代码
@Slf4j
@SpringBootApplication
public class CuratorDemoApplication implements ApplicationRunner {@Autowiredprivate CuratorFramework client;public static void main(String[] args) {SpringApplication.run(CuratorDemoApplication.class, args);}String dataPath = "/ahao/data";@Overridepublic void run(ApplicationArguments args) throws Exception {log.info("。。。。。。。。。。。。。容器初始化完毕。。。。。。。。。。。。。。");String lockPath = "/ahao/lock";TimeUnit.SECONDS.sleep(3);InterProcessReadWriteLock readWriteLock = new InterProcessReadWriteLock(client, lockPath);// 获取读锁InterProcessMutex readLock = readWriteLock.readLock();// 获取写锁InterProcessMutex writeLock = readWriteLock.writeLock();// 保证并发执行,当前时间的秒针部分为30的整数倍则结束循环int seconds = LocalDateTime.now().getSecond();while (seconds/30 != 0){seconds = LocalDateTime.now().getSecond();}for (int j = 0; j < 2; j++) {// 读线程new Thread(() -> {for (int i = 0; i < 10; i++) {try {// 加锁if (readLock.acquire(3, TimeUnit.SECONDS)) {doLock(false);}} catch (Exception e) {throw new RuntimeException(e);} finally {// 释放锁try {readLock.release();} catch (Exception e) {throw new RuntimeException(e);}}}}, "读线程"+j).start();}// 写线程new Thread(() -> {for (int i = 0; i < 5; i++) {try {// 加锁if (writeLock.acquire(3, TimeUnit.SECONDS)) {doLock(true);}} catch (Exception e) {throw new RuntimeException(e);} finally {// 释放锁try {writeLock.release();} catch (Exception e) {throw new RuntimeException(e);}}}}, "写线程").start();}/*** 加操作* @param isAdd true 表示加1,false 表示不加,查询数据* @return* @throws Exception*/public void doLock(boolean isAdd) throws Exception {// 获取数据节点中的值byte[] bytes = client.getData().forPath(dataPath);Integer integer = Integer.valueOf(new String(bytes));if (isAdd) {// 设置新值client.setData().forPath(dataPath, String.valueOf(integer + 1).getBytes(StandardCharsets.UTF_8));log.info("加1操作后:{}", integer);} else {log.info("查询数据:{}", integer);}}
}
输出日志
可以观察到,读线程所查询的数据存在重复数据,说明了在同一时刻可以加多个读锁,而写线程不会出现重复数据,只能有一个线程可以获取到写锁。
实例c1:
2024-01-16 INFO 64462 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:40
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:41
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:42
2024-01-16 INFO 64462 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:42
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:43
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:44
2024-01-16 INFO 64462 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:44
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:45
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:46
2024-01-16 INFO 64462 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:46
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:47
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:48
2024-01-16 INFO 64462 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:48
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:49
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64462 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
实例c2:
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:40
2024-01-16 INFO 64453 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:41
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:42
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:42
2024-01-16 INFO 64453 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:43
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:44
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:44
2024-01-16 INFO 64453 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:45
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:46
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:46
2024-01-16 INFO 64453 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:47
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:48
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:48
2024-01-16 INFO 64453 --- [ 写线程] com.ahao.demo.CuratorDemoApplication : 加1操作后:49
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程1] com.ahao.demo.CuratorDemoApplication : 查询数据:50
2024-01-16 INFO 64453 --- [ 读线程0] com.ahao.demo.CuratorDemoApplication : 查询数据:50
6.共享信号量
对 JUC 熟悉的读者应该了解Semaphore(信号量)。Semaphore是一种用于实现同步的对象,通常用于限制对共享资源的并发访问。Semaphore可以用来控制进入临界区的线程数量,通过使用Semaphore,可以防止过多线程同时访问共享资源,从而避免出现资源竞争和死锁等问题。
而Curator中的Semaphore和JUC中的Semaphore,如出一辙,只是一个应用于分布式场景,一个应用于进程(服务器)内部。
6.1.锁对象
类路径:org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2
公开构造方法如下:
/*** @param client 当前客户端实例* @param path 锁节点路径* @param maxLeases 允许线程进入临界区的最大数量(许可证数量)*/public InterProcessSemaphoreV2(CuratorFramework client, String path, int maxLeases);/*** @param client 当前客户端实例* @param path 锁节点路径* @param count 用于监听许可证数量*/public InterProcessSemaphoreV2(CuratorFramework client, String path, SharedCountReader count);
6.2.信号量抢占
测试场景:有两台服务实例 C1,C2。C1和C2个有3个线程并发执行。但是有2个许可证,每个线程一次只能获取一个许可证,获取成功则执行4s耗时操作(睡眠4s),然后释放锁。
测试代码
@Overridepublic void run(ApplicationArguments args) throws Exception {log.info("。。。。。。。。。。。。。容器初始化完毕。。。。。。。。。。。。。。");String lockPath = "/ahao/lock";TimeUnit.SECONDS.sleep(3);// 创建共享信号量InterProcessSemaphoreV2 semaphoreV2 = new InterProcessSemaphoreV2(client,lockPath,2);// 保证并发执行,当前时间的秒针部分为30的整数倍则结束循环int seconds = LocalDateTime.now().getSecond();while (seconds/30 != 0){seconds = LocalDateTime.now().getSecond();}// 创建线程争夺许可证for (int i = 0; i < 3; i++) {new Thread(() -> {Lease acquire = null;try {acquire = semaphoreV2.acquire(5, TimeUnit.SECONDS);if (acquire != null){log.info("抢到许可证,参与竞争的节点:{}",semaphoreV2.getParticipantNodes());// 睡眠4秒TimeUnit.SECONDS.sleep(4);}else {log.info("抢到许可证失败");}} catch (Exception e) {throw new RuntimeException(e);}finally {if (acquire != null){semaphoreV2.returnLease(acquire);}}}, "线程"+i).start();}}
输出日志
实例c1:
2024-01-16 INFO 4319 --- [ 线程2] com.ahao.demo.CuratorDemoApplication : 抢到许可证
2024-01-16 INFO 4319 --- [ 线程1] com.ahao.demo.CuratorDemoApplication : 抢到许可证
2024-01-16 INFO 4319 --- [ 线程0] com.ahao.demo.CuratorDemoApplication : 抢到许可证失败
实例c2:
2024-01-16 INFO 4307 --- [ 线程0] com.ahao.demo.CuratorDemoApplication : 抢到许可证
2024-01-16 INFO 4307 --- [ 线程2] com.ahao.demo.CuratorDemoApplication : 抢到许可证
2024-01-16 INFO 4307 --- [ 线程1] com.ahao.demo.CuratorDemoApplication : 抢到许可证失败
7.多共享锁
表示将多个锁合并为一个锁。在获取多共享锁时,必须获取其内部所有的锁,才算获取成功,否则释放所有已获取的锁。同样调用释放锁方法时,会释放所有的锁。
7.1.锁对象
类路径:org.apache.curator.framework.recipes.locks.InterProcessMultiLock
公开构造方法如下:
/*** @param client 当前客户端实例* @param path 多个锁节点路径*/public InterProcessMultiLock(CuratorFramework client, List<String> paths);/*** @param locks 多个锁对象*/public InterProcessMultiLock(List<InterProcessLock> locks);
7.2.获取共享锁
测试场景:有一台服务实例 C1,启动3个线程并发执行,抢占同一个共享锁。
测试代码
@Overridepublic void run(ApplicationArguments args) throws Exception {log.info("。。。。。。。。。。。。。容器初始化完毕。。。。。。。。。。。。。。");String lockPath = "/ahao/lock";String lockPath2 = "/ahao/lock2";TimeUnit.SECONDS.sleep(3);// 创建锁1InterProcessMutex mutex = new InterProcessMutex(client,lockPath);// 创建锁2InterProcessMutex mutex2 = new InterProcessMutex(client,lockPath2);// 创建共享锁InterProcessMultiLock multiLock = new InterProcessMultiLock(List.of(mutex,mutex2));for (int i = 0; i < 3; i++) {new Thread(()->{try {if (multiLock.acquire(5, TimeUnit.SECONDS)) {log.info("获取到锁");TimeUnit.SECONDS.sleep(3);}else {log.info("获取失败");}} catch (Exception e) {throw new RuntimeException(e);} finally {try {multiLock.release();} catch (Exception e) {throw new RuntimeException(e);}}},"线程"+i).start();}}
输出日志
可见在第3个线程获取锁时,由于没有获取到/ahao/lock2
对应的锁对象导致的超时。
2024-01-16 INFO 14352 --- [ 线程1] com.ahao.demo.CuratorDemoApplication : 获取到锁
2024-01-16 INFO 14352 --- [ 线程2] com.ahao.demo.CuratorDemoApplication : 获取到锁
2024-01-16 INFO 14352 --- [ 线程0] com.ahao.demo.CuratorDemoApplication : 获取失败
Exception in thread "线程0" java.lang.RuntimeException: java.lang.Exception: java.lang.IllegalMonitorStateException: You do not own the lock: /ahao/lock2at com.ahao.demo.CuratorDemoApplication.lambda$run$0(CuratorDemoApplication.java:70)at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.Exception: java.lang.IllegalMonitorStateException: You do not own the lock: /ahao/lock2at org.apache.curator.framework.recipes.locks.InterProcessMultiLock.release(InterProcessMultiLock.java:169)at com.ahao.demo.CuratorDemoApplication.lambda$run$0(CuratorDemoApplication.java:68)... 1 more
Caused by: java.lang.IllegalMonitorStateException: You do not own the lock: /ahao/lock2at org.apache.curator.framework.recipes.locks.InterProcessMutex.release(InterProcessMutex.java:140)at org.apache.curator.framework.recipes.locks.InterProcessMultiLock.release(InterProcessMultiLock.java:158)... 2 more
相关文章:

ZooKeeper 实战(五) Curator实现分布式锁
文章目录 ZooKeeper 实战(五) Curator实现分布式锁1.简介1.1.分布式锁概念1.2.Curator 分布式锁的实现方式1.3.分布式锁接口 2.准备工作3.分布式可重入锁3.1.锁对象3.2.非重入式抢占锁测试代码输出日志 3.3.重入式抢占锁测试代码输出日志 4.分布式非可重入锁4.1.锁对象4.2.重入…...
基于kubernetes部署MySQL主从环境
部署方式 通过部署mysql主从容器,配置主从pod之间数据同步。 配置数据库访问的密码 创建 Mysql 密码的 Secret [rootk8s-master1 master]# kubectl create secret generic mysql-password --namespaceapp --from-literalmysql_root_passwordroot secret/mysql-pas…...

【JAVA语言-第13话】异常处理 之 try-catch-finally,throws,throw关键字的详细解析
目录 异常处理 1.1 概述 1.2 异常分类 1.3 异常处理 1.3.1 throws 1.3.2 try-catch 1.3.3 finally代码块 1.3.4 throw关键字 1.3.5 throw和throws的区别 1.4 自定义异常 1.4.1 概述 1.4.2 定义 1.4.3 自定义异常练习 异常处理 1.1 概述 在Java中,异常…...
ChatGPT4.0 >ChatGPT 3.5 > 文心一言
文章目录 前言一、ChatGPT4.0与ChatGPT3.5相比具有以下优点:二、ChatGPT和文心一言相比具有以下优点:总结 前言 ChatGPT是一种基于自然语言处理的对话型人工智能模型,由OpenAI开发。它是使用了大规模的语料库进行无监督学习的结果࿰…...

Linux 入门命令大全汇总 + Linux 集锦大全 【20240115】
文章目录 Linux 入门命令大全汇总Linux 集锦大全更多信息 Linux 入门命令大全汇总 别有一番风趣的alias 刚刚好合适的 apropos 命令 迷你计算器 bc 可看黄道吉日的 cal 全文可查看: Linux入门命令大全全文 Linux 集锦大全 linux终端中最漂亮的几款字体介绍及…...

【Web】NSSCTF Round#16 Basic个人wp(全)
出题友好,适合手生复健。 目录 ①RCE但是没有完全RCE ②了解过PHP特性吗 ①RCE但是没有完全RCE 上来就是一段md5八股 (string)就是不让用数组了,然后强比较需要md5碰撞 ?md5_1%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc…...

【目标跟踪】跨相机如何匹配像素
文章目录 前言一、计算思路二、代码三、结果 前言 本本篇博客介绍一种非常简单粗暴的方法,做到跨相机像素匹配。已知各相机内外参,计算共视区域像素投影(不需要计算图像特征)。废话不多说,直接来,见下图。…...
Python 发微信:实现自动化沟通的利器
引言: 在当今信息爆炸的时代,微信已经成为人们日常生活中不可或缺的沟通工具。然而,手动发送微信消息往往耗时耗力,尤其是在需要频繁发送消息的场景下。为了提高工作效率和便利性,我们可以利用 Python 编程语言来实现自…...
计算机网络——HTTP协议
1. HTTP的概述 HTTP(超文本传输协议),定义在RFC2616中,是用于分布式和协作式多媒体系统之间交互的应用层通信协议。 1.1 无状态 HTTP是一个无状态协议,意味着它不保存先前交互的记录。每个请求都独立于其他请求处理。…...

QT上位机开发(利用tcp/ip访问plc)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 plc是工控领域很重要的一个器件。简单的plc一般就是对io进行控制,但是复杂的plc,还可以控制电机、变频器,在工业…...

MySQL-多表连接查询
🎉欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克🍹 ✨博客主页:小小恶斯法克的博客 🎈该系列文章专栏:重拾MySQL 🍹文章作者技术和水平很有限,如果文中出现错误&am…...

Qt第二周周二作业
代码: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);~Widget();void paintEvent(…...
docker 学习命令整理
文章目录 docker 学习命令整理(积累中...)0. 启动/停止1. 运行2. 查看运行中docker3. 删除指定container4. 查看本地镜像5. 拉取指定镜像6. 新起终端进入同一container7. 取消sudo8. 查看docker状态9. 查看docker存储10.删除镜像11.删除容器12. qemu12.1 安装12.2 卸载qemu 附&…...

windows安装RabbitMq,修改数据保存位置
1、先安装Erlang, Erlang和RabbitMQ有版本对应关系。 官网RabbitMQ与Erlang版本对应RabbitMQ Erlang Version Requirements — RabbitMQ 2、安装RabbitMQ。 3、修改数据保存地址。找到安装目录下的sbin文件夹,找到rabbitmq-env.bat,编辑文件…...
Redis面试题18
Redis 支持集群模式吗?如何实现 Redis 的集群? 答:是的,Redis 支持集群模式,并提供了 Redis Cluster 来实现分布式数据存储和高可用性。 Redis Cluster 是通过将数据分散到多个节点上来实现的,每个节点都拥…...
python实现文件批量分发
在Python中实现文件的批量分发通常涉及到文件的读取、网络通信以及目标系统上的文件写入。这里有几种方法来实现这一功能,但最常见的方法之一是使用FTP(文件传输协议)或SSH(安全外壳协议)。以下是使用Python通过SSH进行文件批量分发的一个基本示例。这里使用了paramiko库,…...

分销商城多端uniapp 可编译5端 - 等级提现额度
等级提现额度 等级提现额度是一种常见的财务管理策略,通常用于在线平台、金融服务或游戏中,用于控制不同等级用户的提现限额。这样的机制有助于平台管理资金流动性,防范欺诈,并鼓励用户提升他们的活跃度或忠诚度。以下是一个简单的…...
蓝桥杯基础知识5 unique()
蓝桥杯基础知识5 unique() #include <bits/stdc.h>int main(){std::vector<int> vec {1,1,2,2,3,3,3,4,4,5};auto it std::unique(vec.begin(), vec.end());vec.erase(it, vec.end());//vec.erase(unique(vec.begin(),vec.end()),vec.end(…...

设计一个抽奖系统
👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家📕系列专栏:Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术🔥如果感觉博主的文章还不错的…...

IntelliJ IDEA使用学习
一、安装教程 网上自行下载,CSDN不然过审二、使用教程 2.1 快捷键操作与设置 设置 Setting——>按键映射——>选择顺手的系统快捷键 编写代码 CtrlShift Enter,语句完成。 “!”,否定完成,输入表达式时按 …...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...