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

第17章 读写锁分离设计模式(Java高并发编程详解:多线程与系统设计)

1.场景描述

对资源的访问一般包括两种类型的动作——读和写(更新、删除、增加等资源会发生变化的动作),如果多个线程在某个时刻都在进行资源的读操作,虽然有资源的竞争,但是这种竞争不足以引起数据不一致的情况发生,那么这个时候直接采用排他的方式加锁,就显得有些简单粗暴了。表1将两个线程对资源的访问动作进行了枚举,除了多线程在同一时间都进行读操作时不会引起冲突之外,其余的情况都会导致访问的冲突,需要对资源进行同步处理。

2.读写分离程序设计

2.1 接口定义

读写锁的类图

1.Lock接口定义
public interface Lock {// 获取显示锁, 没有获得锁的线程将被堵塞void lock() throws InterruptedException;// 释放获取的锁void unlock();
}

Lock接口定义了锁的基本操作, 加锁和解锁, 显式锁的操作强烈建议与try finally语句块一起使用,加锁和解锁说明如下。

  • lock() :当前线程尝试获得锁的拥有权, 在此期间有可能进入阻塞。
  • unlock() :释放锁, 其主要目的就是为了减少reader或者writer的数量。
2.ReadWriteLock接口定义

ReadWrite Lock虽然名字中有lock, 但是它并不是lock, 它主要是用于创建read lock和write lock的, 并且提供了查询功能用于查询当前有多少个reader和writer以及waiting中的writer, 根据我们在前文中的分析, 如果reader的个数大于0, 那就意味着writer的个数等于0, 反之writer的个数大于0(事实上writer最多只能为1) , 则reader的个数等于0,由于读和写,写和写之间都存在着冲突,因此这样的数字关系也就不奇怪了。

readLock() :该方法主要用来获得一个Read Lock。
writeLock() :同read Lock类似, 该方法用来获得Write Lock。
getWriting Writers) :获取当前有多少个线程正在进行写的操作, 最多是1个。
getWaiting Writers() :获取当前有多少个线程由于获得写锁而导致阻塞。
getReading Readers() :获取当前有多少个线程正在进行读的操作。

2.2程序实现

1.ReadWriteLockImpl

相对于Lock, ReadWrite Lock Impl更像是一个工厂类, 可以通过它创建不同类型的锁,我们将ReadWrite Lock Impl设计为包可见的类, 其主要目的是不想对外暴露更多的细节,在ReadWrite Lock Impl中还定义了非常多的包可见方法, 代码所示

public class ReadWriteLockImpl implements ReadWriteLock{// 定义对象锁private final Object MUTEX = new Object();// 当前有多少个线程正在写入private int writingWriters = 0;// 当前有多少个线程正在等待写入private int waitingWriters = 0;// 当前有多少个线程正在readprivate int readingReaders = 0;// read 和 write 的偏好设置private boolean preferWriter;// 默认情况下preferWrite为truepublic ReadWriteLockImpl() {this(true);}// 构造ReadWriteLockImpl并且传入preferWriterpublic ReadWriteLockImpl(boolean preferWriter) {this.preferWriter = preferWriter;}public Object getMUTEX() {return MUTEX;}public int getWaitingWriters() {return waitingWriters;}public boolean getPreferWriter() {return preferWriter;}// 创建读锁@Overridepublic Lock readLock() {return new ReadLock(this);}// 创建写锁@Overridepublic Lock writeLock() {return new WriteLock(this);}// 使写线程的数量增加void incrementWritingWriters() {this.writingWriters++;}// 使等待写入的线程数量增加void incrementWaitingWriters() {this.waitingWriters++;}// 使读线程的数量增加void incrementReadingReaders() {this.readingReaders++;}// 使写线程的数量减少void decrementWritingWriters() {this.writingWriters--;}// 使等待获取写入锁的数量减一void decrementWaitingWriters() {this.waitingWriters--;}// 使读取线程的数量减少void descementReadingReaders() {this.readingReaders--;}@Overridepublic int getWritingWriters() {return this.writingWriters;}@Overridepublic int getReadingReaders() {return this.readingReaders;}void changePrefer(boolean preferWriter) {this.preferWriter = preferWriter;}
}

虽然我们在开发一个读写锁,但是在实现的内部也需要一个锁进行数据同步以及线程之间的通信, 其中MUTEX的作用就在于此, 而prefer Writer的作用在于控制倾向性, 一般来说读写锁非常适用于读多写少的场景, 如果prefer Writer为false, 很多读线程都在读数据,那么写线程将会很难得到写的机会。

2.ReadLock

读锁是Lock的实现, 同样将其设计成包可见以透明其实现细节, 让使用者只用专注于对接口的调用,代码如所示

public class ReadLock implements Lock{private final ReadWriteLockImpl readWriteLock;ReadLock(ReadWriteLockImpl readWriteLock) {this.readWriteLock = readWriteLock;}@Overridepublic void lock() throws InterruptedException {// 使用Mutex 作为 锁synchronized (readWriteLock.getMUTEX()) {// 若此时有线程在进行写操作,或者有写线程在等待并且偏向写锁的标识为// true时,就会无法获得读锁,只能被挂起while(readWriteLock.getWritingWriters() > 0|| (readWriteLock.getPreferWriter() && readWriteLock.getWritingWriters() > 0 )) {readWriteLock.getMUTEX().wait();}readWriteLock.incrementReadingReaders();}}@Overridepublic void unlock() {// 使用Mutex作为锁,并且进行同步synchronized (readWriteLock.getMUTEX()) {// 释放锁的过程就是使得当前reading的数量减一// 将perferWriter设置为true,可以使得writer线程获得更多的机会// 通知唤醒与Mutex关联monitor waitset中的线程readWriteLock.descementReadingReaders();readWriteLock.changePrefer(true);readWriteLock.getMUTEX().notifyAll();}}
}
  • 当没有任何线程对数据进行写操作的时候,读线程才有可能获得锁的拥有权,当然除此之外,为了公平起见,如果当前有很多线程正在等待获得写锁的拥有权,同样读线程将会进入Mutex的wait set中, reading Reader的数量将增加。
  • 读线程释放锁, 这意味着reader的数量将减少一个, 同时唤醒wait中的线程, reader唤醒的基本上都是由于获取写锁而进入阻塞的线程,为了提高写锁获得锁的机会,需要将prefer Writer修改为true
3.WriteLock

写锁是Lock的实现, 同样将其设计成包可见以透明其实现细节, 让使用者只用专注于对接口的调用,由于写-写冲突的存在,同一时间只能由一个线程获得锁的拥有权,代码所示。

public class WriteLock implements Lock{private final ReadWriteLockImpl readWriteLock;public WriteLock(ReadWriteLockImpl readWriteLock) {this.readWriteLock = readWriteLock;}@Overridepublic void lock() throws InterruptedException {synchronized (readWriteLock.getMUTEX()) {try {// 首先使等待获取写入锁的数字加一readWriteLock.incrementWritingWriters();// 如果此时有其他线程正在进行读操作,或者写操作,那么当前线程将被挂起while(readWriteLock.getReadingReaders() > 0|| readWriteLock.getWritingWriters() > 0) {readWriteLock.getMUTEX().wait();}} finally {// 成功获取到了写入锁,使得等待获取写入锁的计数减一this.readWriteLock.decrementWaitingWriters();}// 将正在写入的线程数量加一readWriteLock.incrementWritingWriters();}}@Overridepublic void unlock() {synchronized (readWriteLock.getMUTEX() ) {// 减少正在写入锁的线程计数器readWriteLock.decrementWritingWriters();// 将偏好状态修改为false, 可以使得读锁被最快速的获得readWriteLock.changePrefer(false);// 通知唤醒其他在Mutex monitor waitset中的线程readWriteLock.getMUTEX().notifyAll();}}
}

  • 当有线程在进行读操作或者写操作的时候,若当前线程试图获得锁,则其将会进入MUTEX的wait set中而阻塞, 同时增加waiting Writer和writing Writer的数量, 但是当线程从wait set中被激活的时候waiting Writer将很快被减少。
  • 写释放锁, 意味着writer的数量减少, 事实上变成了0, 同时唤醒wait中的线程,并将prefer Writer修改为false, 以提高读线程获得锁的机会。

3.读写锁的使用

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;public class ShareData {// 定义共享数据(资源)private final List<Character> container = new ArrayList<>();//构造ReadWriteLockprivate final ReadWriteLock readWriteLock = ReadWriteLock.readWriteLock();// 创建读取锁private final Lock readLock = readWriteLock.readLock();// 创建写入锁private final Lock writeLock = readWriteLock.writeLock();private final int length;public ShareData(int length) {this.length = length;for(int i = 0; i < length; i++) {container.add(i, 'c');}}public char[] read() throws InterruptedException {try {// 首先使用读锁进行lockreadLock.lock();char[] newBuffer = new char[length];for(int i = 0; i < length; i++) {newBuffer[i] = container.get(i);}slowly();return newBuffer;} finally {// 当操作结束之后,将锁释放readLock.unlock();}}public void write(char c) throws InterruptedException {try {//使用写锁进行lockwriteLock.lock();for(int i = 0; i < length; i++ ) {this.container.add(i, c);}slowly();}finally {// 当所有的操作都完成之后,对写锁进行释放writeLock.unlock();}}// 简单模拟操作的耗时private void slowly() {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}

ShareData中涉及了对数据的读写操作, 因此它是需要进行线程同步控制的。首先, 创建一个ReadWrite Lock工厂类, 然后用该工厂分别创建ReadLock和WriteLock的实例, 在read方法中使用Read Lock对其进行加锁, 而在write方法中则使用WriteLock, 的程序则是关于对ShareData的使用。

public class ReadWriteLockTest {this is the example for read write lockprivate final static String text = "this";public static void main(String[] args) {// 定义共享数据final ShareData shareData = new ShareData(50);// 创建两个线程进行数据写操作for(int i = 0; i < 2; i++) {new Thread(() -> {for(int index = 0; index < text.length(); index++ ) {try {char c= text.charAt(index);shareData.write(c);System.out.println(Thread.currentThread() + " write " + c);}catch (InterruptedException e) {e.printStackTrace();}}}).start();// 创建10个线程进行数据读操作for(int i1 = 0; i1 < 10; i1++ ) {new Thread( ()-> {while(true) {try {System.out.println(Thread.currentThread() + " read " + new String(shareData.read()));} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}}
}

相关文章:

第17章 读写锁分离设计模式(Java高并发编程详解:多线程与系统设计)

1.场景描述 对资源的访问一般包括两种类型的动作——读和写(更新、删除、增加等资源会发生变化的动作)&#xff0c;如果多个线程在某个时刻都在进行资源的读操作&#xff0c;虽然有资源的竞争&#xff0c;但是这种竞争不足以引起数据不一致的情况发生&#xff0c;那么这个时候…...

硬盘修复后,文件隐身之谜

在数字时代&#xff0c;硬盘作为数据存储的重要载体&#xff0c;承载着无数珍贵的信息与回忆。然而&#xff0c;当硬盘遭遇故障并经过修复后&#xff0c;有时我们会遇到这样一个棘手问题&#xff1a;硬盘修复后&#xff0c;文件却神秘地“隐身”&#xff0c;无法正常显示。这一…...

Ollama+ page Assist或Ollama+AnythingLLM 搭建本地知识库

参考&#xff1a;【AI】10分钟学会如何用RAG投喂数据给你的deepseek本地模型&#xff1f;_哔哩哔哩_bilibili 方法一&#xff1a;Ollama page Assist 本地知识库 ***下方操作比较精简&#xff0c;详情参考&#xff1a;Ollama 部署本地大语言模型-CSDN博客 1.下载Ollama 2.O…...

树莓派5添加摄像头 在C++下调用opencv

由于树莓派5 os系统升级,正常libcamera创建对象每次失败。 改如下方法成功。 1 创建管道 rpicam-vid -t 0 --codec mjpeg -o udp://127.0.0.1:8554 > /dev/null 2>&1 2 opencv从管道里读取 #include <opencv2/opencv.hpp> #include <iostream>int mai…...

redis之RDB持久化过程

redis的rdb持久化过程 流程图就想表达两点&#xff1a; 1.主进程会fork一个子进程&#xff0c;子进程共享主进程内存数据(fork其实是复制页表)&#xff0c;子进程读取数据并写到新的rdb文件&#xff0c;最后替换旧的rdb文件。 2.在持久化过程中主进程接收到用户写操作&#x…...

Linux后台运行进程

linux 后台运行进程&#xff1a;& , nohup-腾讯云开发者社区-腾讯云 进程 &&#xff0c;后台运行&#xff0c;结束终端退出时结束进程。 nohup 进程 &&#xff0c;后台运行&#xff0c;结束终端后依然保持运行。...

webpack配置方式

1. 基本配置文件 (webpack.config.js)&#xff08;导出一个对象&#xff09; 最常见的方式是通过 webpack.config.js 文件来配置 Webpack&#xff0c;导出一个对象。你可以在这个文件中导出一个配置对象&#xff0c;指定入口、输出、加载器、插件等。 // webpack.config.js m…...

123,【7】 buuctf web [极客大挑战 2019]Secret File

进入靶场 太熟悉了&#xff0c;有种回家的感觉 查看源代码&#xff0c;发现一个紫色文件 点下看看 点secret 信息被隐藏了 要么源代码&#xff0c;要么抓包 源代码没有&#xff0c;抓包 自己点击时只能看到1和3处的文件&#xff0c;点击1后直接跳转3&#xff0c;根本不出…...

OSPF基础(2):数据包详解

OSPF数据包(可抓包) OSPF报文直接封装在IP报文中&#xff0c;协议号89 头部数据包内容&#xff1a; 版本(Version):对于OSPFv2&#xff0c;该字段值恒为2(使用在IPV4中)&#xff1b;对于OSPFv3&#xff0c;该字段值恒为3(使用在IPV6中)。类型(Message Type):该OSPF报文的类型。…...

Vue 入门到实战 八

第8章 组合API与响应性 目录 8.1 响应性 8.1.1 什么是响应性 8.1.2 响应性原理 8.2 为什么使用组合API 8.3 setup组件选项 8.3.1 setup函数的参数 8.3.2 setup函数的返回值 8.3.3 使用ref创建响应式引用 8.3.4 setup内部调用生命周期钩子函数 8.4 提供/注入 8.4.1 …...

【学习总结|DAY036】Vue工程化+ElementPlus

引言 在前端开发领域&#xff0c;Vue 作为一款流行的 JavaScript 框架&#xff0c;结合 ElementPlus 组件库&#xff0c;为开发者提供了强大的构建用户界面的能力。本文将结合学习内容&#xff0c;详细介绍 Vue 工程化开发流程以及 ElementPlus 的使用&#xff0c;助力开发者快…...

HTML之CSS三大选择器

HTML之CSS三大选择器 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><st…...

理解链接:加载二进制动态库

理解链接&#xff1a;加载二进制动态库 文章目录 理解链接&#xff1a;加载二进制动态库前情提要基本方式1 - 显式连接 dlopen基本方式 2 - 隐式链接 compile link ld衍生方式 3 - 弱链接 weak linking衍生方式 4 - dlmopen 加载到独立命名空间调试所有符号 补充知识1. 动态库…...

ASP.NET Core中Filter与Middleware的区别

中间件是ASP.NET Core这个基础提供的功能&#xff0c;而Filter是ASP.NET Core MVC中提供的功能。ASP.NET Core MVC是由MVC中间件提供的框架&#xff0c;而Filter属于MVC中间件提供的功能。 区别 中间件可以处理所有的请求&#xff0c;而Filter只能处理对控制器的请求&#x…...

《语义捕捉全解析:从“我爱自然语言处理”到嵌入向量的全过程》

首先讲在前面&#xff0c;介绍一些背景 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09; 是一种结合了信息检索与语言生成模型的技术&#xff0c;通过从外部知识库中检索相关信息&#xff0c;并将其作为提示输入给大型语言模型&#xff…...

大规模多准则决策模型构建详细方案

第二阶段&#xff1a;大规模多准则决策模型构建详细方案 目标 基于消费者群体偏好和个体交互数据&#xff0c;构建动态、可扩展的多准则决策模型&#xff0c;实现实时个性化产品排序。 一、技术架构设计 1. 系统架构图 [用户交互层] → (React前端) ↓ [API服务层] → (…...

Rust 语言:变革关键任务软件的新力量

软件无处不在&#xff0c;从手表、烤箱、汽车&#xff0c;甚至可能是牙刷中都有它的身影。更重要的是&#xff0c;软件控制着关乎生死的系统&#xff0c;如飞机、医疗设备、电网系统和银行基础设施等。如果软件工程师稍有疏忽&#xff0c;软件缺陷和漏洞可能导致数十亿美元的损…...

Linux特权组全解析:识别GID带来的权限提升风险

组ID&#xff08;Group ID&#xff0c;简称 GID&#xff09;是Linux系统中用来标识不同用户组的唯一数字标识符。每个用户组都有一个对应的 GID&#xff0c;通过 GID&#xff0c;系统能够区分并管理不同的用户组。 在Linux系统中&#xff0c;系统用户和组的配置文件通常包括以…...

安卓/ios脚本开发按键精灵经验小分享

1. 程序的切换 我们经常碰到这样的需求&#xff1a;打开最近的应用列表&#xff0c;选取我们想要的程序。但是每个手机为了自己的风格&#xff0c;样式都有区别&#xff0c;甚至连列表的滑动方向都不一样&#xff0c;我们很难通过模拟操作来识别点击&#xff0c;那么我们做的只…...

机器学习在癌症分子亚型分类中的应用

学习笔记&#xff1a;机器学习在癌症分子亚型分类中的应用——Cancer Cell 研究解析 1. 文章基本信息 标题&#xff1a;Classification of non-TCGA cancer samples to TCGA molecular subtypes using machine learning发表期刊&#xff1a;Cancer Cell发表时间&#xff1a;20…...

DeepSeek本地部署保姆级教程

由于DeepSeek近期遭受攻击&#xff0c;又加上用户访问量较大&#xff0c;导致总是服务不可用&#xff0c;让人十分窝火。有没有好的解决办法呢&#xff1f;答案是自己在电脑端部署一套&#xff0c;这样就不用和别人抢着用了。另外本地部署的好处还有保护隐私与减少延迟。 如果…...

无惧户外复杂环境,安科瑞 AKH-0.66/K-HW 开口式互感器准确测流

​安科瑞 吕梦怡 18706162527 1.产品特点 AKH-0.66/K-HW 系列互感器具有防水功能&#xff0c;可在户外使用&#xff0c;切面端口采用橡胶垫环绕可有效阻止雨水进入。互感器采用注塑技术&#xff0c;将互感器线圈直接在模具中进行注塑&#xff0c;同时二次侧引线采用防水端子…...

玩转Docker | 使用Docker部署httpd服务

玩转Docker | 使用Docker部署httpd服务 前言一、准备工作环境确认检查操作系统准备网站目录和配置文件二、拉取httpd镜像三、运行httpd容器运行容器命令检查容器状态四、验证httpd服务浏览器访问测试错误排查五、容器管理与维护查看容器状态停止和启动容器更新网站内容和配置六…...

MacOS 安装NVM

MacOS 安装NVM 方法一&#xff1a;使用Homebrew安装nvm 打开终端&#xff08;Terminal&#xff09;&#xff0c;输入以下命令安装Homebrew&#xff1a; /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"安装nvm…...

Qt 数据库SQLite 使用【01】基本功能

1.开发背景 Qt 开发过程中难免需要存储数据&#xff0c;可以选择保存到本地文件&#xff0c;但是查找比较麻烦&#xff0c;所以就有了数据库&#xff0c;主要是方便查找数据&#xff0c;增删改查等操作&#xff0c;而 SqLite 属于数据库中轻量级的存在&#xff0c;适合本地数据…...

http状态码:请说说 503 Service Unavailable(服务不可用)的原因以及排查问题的思路

503 Service Unavailable&#xff08;服务不可用&#xff09; 是一种HTTP状态码&#xff0c;表示服务器当前无法处理请求&#xff0c;通常是由于临时性原因导致服务中断。以下是它的常见原因和排查思路&#xff1a; 一、503错误的常见原因 1. 服务器过载 场景&#xff1a;服务…...

58页PPT学习华为面向业务价值的数据治理实践

目录 1. 正文解读... 1 2. 华为数据质量管控的质量度量框架是怎样的?... 2 3. 如何在企业中实施类似华为的数据质量管控...

电脑开机提示按f1原因分析及终极解决方法来了

经常有网友问到一个问题&#xff0c;我电脑开机后提示按f1怎么解决&#xff1f;不管理是台式电脑&#xff0c;还是笔记本&#xff0c;都有可能会遇到开机需要按F1&#xff0c;才能进入系统的问题&#xff0c;引起这个问题的原因比较多&#xff0c;今天小编在这里给大家列举了比…...

DeepSeek模型构建与训练

在完成数据预处理之后,下一步就是构建和训练深度学习模型。DeepSeek提供了简洁而强大的API,使得模型构建和训练变得非常直观。无论是简单的全连接网络,还是复杂的卷积神经网络(CNN)或循环神经网络(RNN),DeepSeek都能轻松应对。本文将带你一步步构建一个深度学习模型,并…...

ProxySQL实现mysql8主从同步读写分离

一、ProxySQL基本介绍 ProxySQL是 MySQL 的高性能、高可用性、协议感知代理。 简单介绍下ProxySQL及其功能和配置&#xff0c;主要包括&#xff1a; 最基本的读/写分离&#xff0c;且方式有多种&#xff1b;可定制基于用户、基于schema、基于语句的规则对SQL语句进行路由&…...

Day38-【13003】短文,树的基本概念,用广义表表示树

文章目录 第五章 树与二叉树第一节 树的基本概念用广义表&#xff0c;也就是集合表示发&#xff0c;来表示树 第五章 树与二叉树 第一节 树的基本概念 因为树是一种层次结构&#xff0c;所以它是一种非线性结构&#xff0c;在实际应用中具有广泛的用途。 日常生活中&#xff…...

LabVIEW与PLC交互

一、写法 写命令立即读出 写命令后立即读出&#xff0c;在同一时间不能有多个地方写入&#xff0c;因此需要在整个写入后读出过程加锁 项目中会存在多个循环并行执行该VI&#xff0c;轮询PLC指令 在锁内耗时&#xff0c;就是TCP读写的实际耗时为5-8ms&#xff0c;在主VI六个…...

MySQL第四次作业

新建数据库 新建表 student表 2.course表 3.sc表 修改Student 表中年龄(sage)字段属性&#xff0c;数据类型由int 改变为smallint alter table student modify sage smallint; 为Course表中Cno 课程号字段设置索引&#xff0c;并查看索引 create index index_cno on cou…...

栈和队列的实现(C语言)

1&#xff1a;栈 1&#xff1a;概念和结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只运行在固定的一段进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守先进后出的原则。 压栈&#xff1a;在栈里面插入…...

(四)QT——QMainWindow——界面菜单设计

目录 前言 QMainWindow 结构 菜单栏 工具栏 状态栏 停靠部件 核心部件 UI 文件创建窗口 总结 前言 QMainWindow 是 Qt 框架中的一个类&#xff0c;主要用于创建桌面应用程序的主窗口。它提供了一个标准的窗口布局&#xff0c;包含菜单、工具栏、状态栏和中心小部件等功…...

MySQL InnoDB引擎 脏读、不可重复读和幻读

在 MySQL 的 InnoDB 存储引擎中&#xff0c;脏读、不可重复读和幻读是并发事务操作时可能出现的数据不一致问题&#xff0c;不同的事务隔离级别对这些问题有不同的处理方式。 1、脏读&#xff08;Dirty Read&#xff09; 定义&#xff1a;一个尚未提交的数据变更的事务&#…...

初阶数据结构:树---堆

目录 一、树的概念 二、树的构成 &#xff08;一&#xff09;、树的基本组成成分 &#xff08;二&#xff09;、树的实现方法 三、树的特殊结构------二叉树 &#xff08;一&#xff09;、二叉树的概念 &#xff08;二&#xff09;、二叉树的性质 &#xff08;三&#…...

判断192.168.1.0/24网络中,当前在线的ip有哪些

需求&#xff1a;判断192.168.1.0/24网络中&#xff0c;当前在线的ip有哪些&#xff0c;并编写脚本打印出来。 [rootopenEuler ~]# cat 1.sh #!/bin/bash for ip in $(seq 1 254); do ping -c 1 -W 1 "192.168.1.$ip" > /dev/null 2>&1 if [ $? …...

初始JavaEE篇 —— Spring Web MVC入门(上)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 RequestMappingg 注解介绍 Postman的介绍与使用 PostMapping 与 GetMapping 注解 构造并接收请求 接收简单参数 接收对象…...

STM32的HAL库开发-通用定时器输入捕获实验

一、通用定时器输入捕获部分框图介绍 1、捕获/比较通道的输入部分(通道1) 首先设置 TIM_CCMR1的CC1S[1:0]位&#xff0c;设置成01&#xff0c;那么IC1来自于TI1&#xff0c;也就是说连接到TI1FP1上边。设置成10&#xff0c;那个IC1来自于TI2&#xff0c;连接到TI2FP1上。设置成…...

nodejs:express + js-mdict 网页查询英汉词典,能播放.spx 声音

向 DeepSeek R1 提问&#xff1a; 我想写一个Web 前端网页&#xff0c;后台用 nodejs js-mdict , 实现在线查询英语单词&#xff0c;并能播放.spx 声音文件 1. 项目结构 首先&#xff0c;创建一个项目目录&#xff0c;结构如下&#xff1a; mydict-app/ ├── public/ │ …...

【蓝桥杯嵌入式】5_PWM

全部代码网盘自取 链接&#xff1a;https://pan.baidu.com/s/1PX2NCQxnADxYBQx5CsOgPA?pwd3ii2 提取码&#xff1a;3ii2 1、PWM占空比可调 以往届的赛题举例 将PA6、PA7分别设置为TIM16_CH1和TIM17_CH1 打开TIM16和TIM17&#xff0c;并设置PWM输出模式及其频率 设置占空比初…...

ESM-IF1:从AF2的预测结构中学习逆折叠

作者研究了从蛋白质骨干原子坐标预测蛋白质序列的问题。迄今为止&#xff0c;机器学习解决此问题的方法一直受限于可用的实验测定蛋白质结构的数量。作者使用AlphaFold2为1200万个蛋白质序列预测的结构&#xff0c;从而将训练数据扩充了近三个数量级。相比现有方法&#xff0c;…...

kafka服务端之控制器

文章目录 概述控制器的选举与故障恢复控制器的选举故障恢复 优雅关闭分区leader的选举 概述 在Kafka集群中会有一个或多个broker&#xff0c;其中有一个broker会被选举为控制器&#xff08;Kafka Controler&#xff09;&#xff0c;它负责管理整个集群中所有分区和副本的状态。…...

Redis双写一致性(数据库与redis数据一致性)

一 什么是双写一致性&#xff1f; 当修改了数据库&#xff08;MySQL&#xff09;中的数据&#xff0c;也要同时更新缓存&#xff08;redis&#xff09;中的数据&#xff0c;缓存中的数据要和数据库中的数据保持一致 双写一致性&#xff0c;根据业务对时间上的要求&#xff0c;…...

feign Api接口中注解问题:not annotated with HTTP method type (ex. GET, POST)

Bug Description 在调用Feign api时&#xff0c;出现如下异常&#xff1a; java.lang.IllegalStateException: Method PayFeignSentinelApi#getPayByOrderNo(String) not annotated with HTTPReproduciton Steps 1.启动nacos-pay-provider服务&#xff0c;并启动nacos-pay-c…...

开源2+1链动模式AI智能名片S2B2C商城小程序:突破流量与创意困境的新路径

摘要&#xff1a;本文深入剖析当前互联网行业中流量集中于巨头以及创意边际效应递减的困境&#xff0c;并探讨开源21链动模式AI智能名片S2B2C商城小程序在应对这些困境时所展现的独特优势与应用策略。通过对行业现状的分析以及该小程序功能特点的研究&#xff0c;旨在为企业在艰…...

python编程-内置函数compile(),exec(),complex(),eval()详解

1. compile() 函数 ‌用途‌&#xff1a;将一个字符串源代码编译为字节码对象&#xff0c;这样可以直接被Python解释器执行&#xff0c;或者通过exec()或eval()函数来执行。 ‌参数‌&#xff1a; source&#xff1a;一个字符串或AST&#xff08;抽象语法树&#xff09;对象&am…...

websocket自动重连封装

websocket自动重连封装 前端代码封装 import { ref, onUnmounted } from vue;interface WebSocketOptions {url: string;protocols?: string | string[];reconnectTimeout?: number; }class WebSocketService {private ws: WebSocket | null null;private callbacks: { [k…...

解锁C/C++:链表数据结构的奇幻之旅

目录 一、引言二、链表基础概念2.1 链表是什么2.2 链表的类型三、C 语言实现链表3.1 定义链表节点3.2 创建链表3.3 链表操作3.3.1 遍历链表3.3.2 插入节点3.3.3 删除节点3.3.4 查找节点3.4 完整示例代码四、C++ 实现链表4.1 定义链表节点类4.2 创建链表4.3 链表操作4.3.1 遍历链…...