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

Java并发编程-锁(八)

文章目录

  • Condition的使用和实现
    • 使用
      • add(T t)
    • 实现
      • 等待队列
      • await()
      • signal()
      • signalAll()
  • 总结

Condition的使用和实现

我们知道,任意一个Java Object,都拥有一组监视器方法,主要包括wait()、 wait(long timeout)、notify()以及notifyAll()方法,这些方法和synchronized同步关键字配合,可以 实现等待/通知模式。

Condition接口也提供了类似Object的监视器方法,和Lock配合也可以实现等待/通知模式,但是这两者在使用以及功能上还是有比较大差别的。

Condition定义了等待/通知这两种类型的方法,当前线程调用这些方法时,需要提前获取到 Condition对象关联的锁。而Condition对象是由Lock对象,也就是调用Lock对象的newCondition()方法创建出来的,我们前面讲Lock接口的时候也提到过,换句话说,Condition其实是依赖Lock对象的。

使用

Condition的使用方式比较简单,只是需要注意下在调用方法前先要获取到锁。

下面我们就通过一个有界队列的示例来深入了解下Condition的使用方式。

首先,介绍下有界队列,顾名思义,有界队列就是一种特殊的队列,当队列为空时,队列的获取操作就会阻塞获取线程,直到队列中有新增元素,当队列满了,队列的插入操作就会阻塞插入线程,直到队列出现“空位”。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedQueue<T> {private Object[]  items;// 添加的下标,删除的下标和数据当前的数量private int       addIndex, removeIndex, count;private Lock      lock     = new ReentrantLock();private Condition notEmpty = lock.newCondition();private Condition notFull  = lock.newCondition();public BoundedQueue(int size) {items = new Object[size];}// 添加一个元素,如果数组满了,就让线程进入到等待队列,直到数组有空位public void add(T t) throws InterruptedException {lock.lock();try {while (count == items.length) // 防止虚假唤醒notFull.await();items[addIndex] = t;if (++addIndex == items.length)addIndex = 0;++count;notEmpty.signal();} finally {lock.unlock();}}// 从头删除一个元素,如果数组是空的,则删除线程进入等待状态,直到有新添加元素@SuppressWarnings("unchecked")public T remove() throws InterruptedException {lock.lock();try {while (count == 0) // 防止虚假唤醒notEmpty.await();Object x = items[removeIndex];if (++removeIndex == items.length)removeIndex = 0;--count;notFull.signal();return (T) x;} finally {lock.unlock();}}
}

add(T t)

BoundedQueue通过add(T t)方法添加一个元素,通过remove()方法移出一个 元素。

首先需要获得锁,目的是确保数组修改的可见性和排他性。当数组数量等于数组长度时, 表示数组已满,就会调用notFull.await(),当前线程随之释放锁并进入等待状态。

如果数组数量不 等于数组长度,表示数组没满,就添加元素到数组中,同时通知等待在notEmpty上的线程,告知数 组中已经有新元素可以获取。

注意,在添加和删除方法中使用while循环而不是if判断,目的就是为了防止过早或意外的通知,只有条件符合才能够退出循环。

回想下之前提到的等待/通知的经典范式,两者是非常类似的。

实现

Condition接口的实现 ConditionObject其实是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要 获取相关联的锁。每个Condition对象都包含着一个队列(下面我们就把它叫做等待队列),这个队列是Condition对象实现等待/通知功能的关键所在。

所以呢,要分析Condition的实现,关键内容就是这3个:

等待队列、等待和通知,接下来我们提到的Condition如果没有特别说明都指的是ConditionObject类。

等待队列

等待队列是一个FIFO的队列,在队列中的每个节点都包含了一个线程引用,这个线程就是 在Condition对象上等待的线程,如果一个线程调用了Condition.await()方法,那么这个线程就会 释放锁、构造成节点加入等待队列并进入等待状态。

事实上,这个节点的定义其实复用了同步器中节点 的定义,也就是 AbstractQueuedSynchronizer中的Node类。

一个Condition包含一个等待队列,Condition拥有首节点(firstWaiter)和尾节点 (lastWaiter)这两个属性。当前线程调用Condition.await()方法之后,就会用当前线程来构造节点,并把节点从尾部 加入到等待队列里面,先来看下等待队列的基本结构。

在这里插入图片描述

可以看到,Condition拥有首尾节点的引用,而新增节点只需要把原有的尾节点的nextWaiter 属性指向它,并且更新尾节点就可以了。

注意,上面的节点引用更新的过程并没有使用CAS来保证线程安全,原因就在于调用 await()方法的线程肯定是获取了锁的线程。

对比下 Object的监视器模型,一个对象只有一个同步队列和等待队列,而并发包里的 Lock(更确切地说是同步器)有一个同步队列和多个等待队列,他们是这样对应的。

在这里插入图片描述

Condition的实现其实是同步器的内部类,所以每个Condition实例都能够访问到同步器 提供的方法。

大致了解了等待队列的结构之后,我们来看看等待的实现方式。

await()

调用Condition的await()方法(或者以await开头的方法),会让当前线程进入等待队列并且释放锁,同时线程状态会变为等待状态。而当线程从await()方法返回时,当前线程一定已经获取了和Condition相关联的锁。

如果从队列,也就是同步队列和等待队列的角度看await()方法,当调用await()方法时,相当于同步队列的首节点,也就是获取了锁的节点 移动到了Condition的等待队列里面。

注意,同步队列的首节点并不会直接加入等待队列,而是通过 addConditionWaiter()方法把当前线程构造成一个新的节点 然后再加入到等待队列中

在这里插入图片描述

来看下代码,Condition的await()方法

public final void await() throws InterruptedException {if (Thread.interrupted())//若线程已被中断(例如 Thread.interrupted() 返回 true),直接抛出 InterruptedExceptionthrow new InterruptedException();Node node = addConditionWaiter(); //将当前线程封装为节点,添加到条件变量对应的等待队列尾部//完全释放当前线程持有的锁(注意是彻底释放,包括重入锁的全部计数)。//例如,线程持有锁时多次重入,此方法会将锁状态重置为初始值int savedState = fullyRelease(node);int interruptMode = 0;while (!isOnSyncQueue(node)) { //检查是否被唤醒并移入同步队列LockSupport.park(this);// 挂起线程if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)//检查中断标记 checkInterruptWhileWaiting(node),若中断发生则退出循环break;}//退出等待循环后,通过 acquireQueued(node, savedState) 重新在同步队列中排队获取锁(与普通锁竞争逻辑一致)if (acquireQueued(node, savedState) && interruptMode != THROW_IE)//处理线程重新获取锁过程中可能发生的中断,并调整中断模式interruptMode = REINTERRUPT;if (node.nextWaiter != null) //清理条件队列(Condition Queue)中已被取消的等待节点,保持队列的干净和高效unlinkCancelledWaiters(); if (interruptMode != 0)reportInterruptAfterWait(interruptMode); //上报中断状态
}private void reportInterruptAfterWait(int interruptMode)throws InterruptedException {if (interruptMode == THROW_IE)throw new InterruptedException(); // 等待过程的中断直接抛出异常else if (interruptMode == REINTERRUPT)selfInterrupt(); // 重新获取锁过程的中断,设置中断标记位,由上层处理
}

调用这个方法的线程必须是成功获取了锁的线程,也就是同步队列中的首节点,这个方法会把当前线程构造成节点并加入等待队列里面,然后释放同步状态,唤醒同步队列中的后继节点,然后当前线程就会进入等待状态。

而当等待队列中的节点被唤醒,被唤醒节点的线程就会开始尝试获取同步状态。

中断处理:

假设线程A调用 await() 后被唤醒,进入同步队列排队等待锁:

  • 若此时线程再次被中断(如 interrupt() 调用),则 acquireQueued() 返回 true

  • 若原 interruptMode0(无中断或已处理),则更新为 REINTERRUPT,后续在 reportInterruptAfterWait() 中调用 selfInterrupt() 恢复中断状态

类比说明(食堂排队场景)

在这里插入图片描述

  1. 点餐时发现没菜:
    你(线程)在食堂点餐发现没菜(条件不满足),把座位(锁)让给其他人(释放锁),排到“等菜队伍”(Condition 队列)中等候.

  2. 等待厨师补菜:
    厨师(其他线程)补菜后大喊“菜来了!”(调用 signal()),你会被从“等菜队伍”转移到“取餐队伍”(同步队列)。这时你需要重新抢座位(锁)才能开始就餐(继续执行)

signal()

调用 Condition 的 signal() 方法,会唤醒在等待队列中等待时间最⻓的节点, 也就是首节点,在唤醒节点之前,会先把节点移到同步队列里面。

在这里插入图片描述

调用这个方法的前置条件是当前线程必须获取了锁,可以看到 signal() 方法进行了isHeldExclusively()检查,也就是当前线程必须是获取了锁的线程。

看看 signal 方法, 也就是通知方法,代码很简单

public final void signal() {if (!isHeldExclusively()) // 当前线程必须是获取了锁的线程throw new IllegalMonitorStateException();Node first = firstWaiter;if (first != null)doSignal(first); // 唤醒首节点
}

接着获取等待队列的首节点,调用 doSignal 执行唤醒

private void doSignal(Node first) {
// 将条件队列中的节点转移到同步队列(Sync Queue)以唤醒线程do {if ( (firstWaiter = first.nextWaiter) == null) // 更新头节点lastWaiter = null; // 重置尾节点first.nextWaiter = null; // 断开链接} while (!transferForSignal(first) &&(first = firstWaiter) != null);
}

我们看看是怎么唤醒的? 可以看到,这里是一个循环,循环内部执行的操作就是:

  1. 更新头节点:将 firstWaiter(条件队列的头部指针)指向当前节点的下一个节点 first.nextWaiter

  2. 重置尾节点:若 first 是队列的最后一个节点(nextWaiter == null),则清空 lastWaiter,保持队列一致性.

  3. 断开链接:first.nextWaiter = null 将当前节点从条件队列移除,避免循环引用

循环退出的条件就是: 某个节点成功转移(transferForSignal(first) 返回 true)或者队列遍历结束(无更多节点可处理)。

我们看看它是怎么移动的呢?

final boolean transferForSignal(Node node) {if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))//如果节点状态无法改变,说明已经被cancelreturn false;Node p = enq(node); // 线程安全地移动等待队列的头节点到同步队列int ws = p.waitStatus;if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))LockSupport.unpark(node.thread);return true;
}

通过调用同步器的 enq(Node node) 方法,等待队列中的头节点会被线程 安全地移动到同步队列。这个方法我们之前看过,就不具体看了 。

当节点移动到同步队列后,再使用 LockSupport 唤醒该节点的线程。被唤醒后的线程,会从 await() 方法中的 while 循环中退出 (也就是 isOnSyncQueue(Node node) 方法 返回 true,节点已经在同步队列中),进而调用同步器的acquireQueued()方法加入到获取同步状 态的竞争中。

最后 ,成功获取同步状态 (或者说锁) 之后,被唤醒的线程会从调用的 await() 方法中返回,这个时候线程已经成功地获取了锁。

signalAll()

public final void signalAll() {if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;if (first != null)doSignalAll(first);
}
private void doSignalAll(Node first) {lastWaiter = firstWaiter = null;do {Node next = first.nextWaiter; //记录当前节点的下一个节点 nextfirst.nextWaiter = null;//断开当前节点的链接,防止残留引用干扰遍历transferForSignal(first);//将节点传入同步队列,唤醒对应线程first = next;//移动至下一个节点} while (first != null);
}

看看 Condition的signalAll()方法,它其实就相当于对等待队列中的每个节点 都执行一次 signal() 方法,效果就是把等待队列中所有节点全部移动到同步队 列当中,并且唤醒每个节点的线程。

总结

这一节主要介绍了Java并发包中和锁相关的API和组件,也通过示例展示了这些API和组件的使用 方式以及需要注意的地方,并且在此基础上我们一起详细地剖析了队列同步器、重入锁、读写锁以及 Condition等 API和组件的实现细节。只有真正理解了这些API和组件的实现细节才能够更加准确地运用它们。

相关文章:

Java并发编程-锁(八)

文章目录 Condition的使用和实现使用add(T t) 实现等待队列await()signal()signalAll() 总结 Condition的使用和实现 我们知道&#xff0c;任意一个Java Object&#xff0c;都拥有一组监视器方法&#xff0c;主要包括wait()、 wait(long timeout)、notify()以及notifyAll()方法…...

idea如何快速生成测试类

点击 code -> generate -> Test...

FPGA笔试题review

今天翻网盘上的旧资料,找到了一套20年9月份在武汉某芯片公司食堂做的笔试题(我在做笔试题,旁边的人在嗦酸辣粉,也算是记忆犹新),借着这套题目,正好也可以捡一捡关于FPGA的基础知识点,算是温故而知新。答案更新中 1、名词解释 (1)FPGA、ASIC (2)CLB、LUT (3)时…...

[C++类和对象]构造函数和析构函数

类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f; 并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6 个默认成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器会…...

Java【网络原理】(5)深入浅出HTTPS:状态码与SSL/TLS加密全解析

目录 1.前言 2.正文 2.1状态码 2.2HTTP与HTTPS的关系 2.3SSL协议 2.3.1对称加密 2.3.2非对称加密 2.3.3中间人攻击 2.3.4校验机制 2.3.4.1证书 2.3.4.2数字签名 1. 数字签名的生成过程 2. 数字签名的验证过程 2.4TLS协议&#xff08;握手过程&#xff09; 3.小结…...

《全球短剧正版授权通道,助力平台出海与流量变现》

正版短剧片源授权&#xff0c;全方位赋能您的内容运营 短剧作为短视频领域的一种重要形式&#xff0c;凭借其紧凑的剧情、鲜明的角色和引人入胜的叙事方式&#xff0c;赢得了广大观众的喜爱。 然而&#xff0c;在短剧市场蓬勃发展的同时&#xff0c;版权问题也日益凸显。为了保…...

17.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--ELK

在微服务中&#xff0c;日志是非常重要的组成部分。它不仅可以帮助我们排查问题&#xff0c;还可以帮助我们分析系统的性能和使用情况。ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;是一个强大的日志分析工具&#xff0c;可以帮助我们收集、存储和分析日志数据…...

Linux系统管理与编程16:PXE自动化安装部署centos7.9操作系统

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 0.准备 1&#xff09;防火墙和SELinux systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/^SELINUX.*/SELINUXdisabled/ /etc/selinux/config (很不好的…...

DAMA第10章深度解析:参考数据与主数据管理的核心要义与实践指南

引言 在数字化转型的浪潮中&#xff0c;数据已成为企业的核心资产。然而&#xff0c;数据孤岛、冗余和不一致问题严重制约了数据价值的释放。DAMA&#xff08;数据管理协会&#xff09;提出的参考数据&#xff08;Reference Data&#xff09;与主数据&#xff08;Master Data&…...

Python+OpenCV打造AR/VR基础框架:从原理到实战的全链路解析

引言&#xff1a;重新定义数字与现实的边界 在元宇宙概念持续升温的当下&#xff0c;AR&#xff08;增强现实&#xff09;与VR&#xff08;虚拟现实&#xff09;技术正成为连接物理世界与数字世界的桥梁。Python凭借其丰富的计算机视觉生态&#xff08;尤其是OpenCV库&#xf…...

PaddleOCR本地部署

构建TestPaddle目录&#xff1a; TestPaddle/ └── PaddleOCR ├── ocr_server.py ├── ch_PP-OCRv4_det_infer.tar ├── ch_PP-OCRv4_rec_infer.tar └── 001.jpg1、安装PaddleOCR 安装 PaddleOCR git clone https://github.com/PaddlePaddle/PaddleOCR.git cd …...

Spring事务融入(REQUIRED)具体实现步骤解析

Spring事务融入(REQUIRED传播行为)是Spring事务管理中最核心的机制&#xff0c;下面我将深入剖析其具体实现步骤和关键代码逻辑。 1. 整体流程概览 事务融入(REQUIRED)的核心逻辑是&#xff1a; 检查当前线程是否存在事务 存在则融入(加入)该事务 不存在则创建新事务 2. …...

Java 开发者 Linux 学习指南

目录 一、引言&#xff1a;为什么 Java 开发者必须掌握 Linux 二、Linux 基础&#xff1a;核心概念与常用命令 &#xff08;一&#xff09;文件系统与目录结构 &#xff08;二&#xff09;权限体系与用户管理 &#xff08;三&#xff09;进程管理与监控 三、在 Linux 上安…...

2025年PMP 学习七 -第5章 项目范围管理 (5.4,5.5,5.6 )

2025年PMP 学习七 -第5章 项目范围管理 5.4 创建 WBS 1.定义与作用 定义把项目可交付成果和项目工作分解成较小的&#xff0c;更易于管理的组件作用对所要交付的内容提供一个结构化的视图 2.输入&#xff0c;输出&#xff0c;工具与技术 3. 创建WBS的依据&#xff08;输入&…...

【LangChain全景指南】构建下一代AI应用的开发框架

目录 &#x1f31f; 前言&#x1f3d7;️ 技术背景与价值&#x1f6a7; 当前技术痛点&#x1f6e0;️ 解决方案概述&#x1f465; 目标读者说明 &#x1f50d; 一、技术原理剖析&#x1f4ca; 核心概念图解&#x1f4a1; 核心作用讲解&#x1f9e9; 关键技术模块说明⚖️ 技术选…...

Linux系统:虚拟文件系统与文件缓冲区(语言级内核级)

本节重点 初步理解一切皆文件理解文件缓冲区的分类用户级文件缓冲区与内核级文件缓冲区用户级文件缓冲区的刷新机制两级缓冲区的分层协作 一、虚拟文件系统 1.1 理解“一切皆文件” 我们都知道操作系统访问不同的外部设备&#xff08;显示器、磁盘、键盘、鼠标、网卡&#…...

Linux进程间信号

目录 信号入门 生活角度中的信号 技术应用角度的信号 信号的发送与记录 信号处理常见方式概述 产生信号 通过终端按键产生 通过系统函数向进程发信号 由软件条件产生信号 由硬件异常产生信号 阻塞信号 信号其他相关常见概念 在内核中的表示 sigset_t 信号集操作…...

数据分析2

五、文件 CSV Comma-Separated Value&#xff0c;逗号分割值。CSV文件以纯文本形式存储表格数据&#xff08;数字和文本&#xff09;。 CSV记录间以某种换行符分隔&#xff0c;每条记录由字段组成&#xff0c;字段间以其他字符或字符串分割&#xff0c;最常用逗号或制表符。…...

每日一题:两个仓库的最低配送费用问题

文章目录 两个仓库的最低配送费用问题一、问题描述二、解题思路&#xff08;一&#xff09;初始假设&#xff08;二&#xff09;差值定义&#xff08;三&#xff09;选择最优&#xff08;四&#xff09;计算答案 三、代码实现四、代码分析&#xff08;一&#xff09;输入处理&a…...

SemanticSplitterNodeParser 和 Sentence-BERT 的区别和联系是什么

这涉及到文本切分&#xff08;chunking&#xff09;与语义向量&#xff08;embedding&#xff09;之间的关系。我们来详细对比&#xff1a; ✅ 1. SemanticSplitterNodeParser 是什么&#xff1f; SemanticSplitterNodeParser 是 llama-index 提供的一种 语义感知的文本切分工…...

Fabric系列 - SoftHSM 软件模拟HSM

在 fabric-ca-server 上使用软件模拟的 HSM(密码卡) 功能 安装 SoftHSMv2 教程 SoftHSMv2 默认的配置文件 /etc/softhsm2.conf默认的token目录 /var/lib/softhsm/tokens/ 初始化和启动fabric-ca-server&#xff0c;需要设置一个管理员用户的名称和密码 初始化令牌 # 初始…...

简易图片编辑工具,支持抠图和替换背景

软件介绍 Photo Retouch是一款由微软官方商店推出的免费图片处理软件&#xff0c;具有抠图、换背景、修复等功能&#xff0c;操作便捷且效率极高&#xff0c;非常值得尝试。 功能详解 这款软件提供五大功能&#xff0c;包括删除物体、快速修复、一键抠图、背景调整和裁剪…...

CountDownLatch 并发编程中的同步利器

CountDownLatch 并发编程中的同步利器 文章目录 CountDownLatch 并发编程中的同步利器一、CountDownLatch 基础概念1.1 什么是 CountDownLatch&#xff1f;1.2 CountDownLatch 的核心方法1.3 基本使用示例 二、CountDownLatch 实战应用2.1 应用场景一&#xff1a;并行任务协调2…...

C++GO语言微服务之用户信息处理

目录 01 01-微服务实现用户注册-微服务端-上 02 02-微服务实现用户注册-微服务端-下 03 03-微服务实现用户注册-web端 04 04-微服务实现用户注册-web端-流程小结 05 05-获取地域信息-读MySQL写Redis入 06 06-获取地域信息-先查redis-没有读MySQL写入 01 07-Cookie简介 0…...

互联网大厂Java求职面试实战:Spring Boot微服务与数据库优化详解

&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&#xff0c;毕业季咱们不慌忙&#xff0c;几百款毕业设计等你选。 ❤️ 3. Python爬虫专栏…...

Linux基本指令(一)

目录 基本指令 pwd指令 cd指令 cd ..​编辑 cd ~ ls指令 ls -l ls -a ls -d touch指令 mkdir指令 rmdir指令 && rm 指令 操作系统是什么呢&#xff1f;一个好的操作系统要具备什么条件呢&#xff1f; 简单来说&#xff0c;操作系统是是一款做软硬件管理的软…...

Java—— 集合 List

List集合的特点 有序&#xff1a;存和取的元素顺序一致 有索引&#xff1a;可以通过索引操作元素 可重复&#xff1a;存储的元素可以重复 List集合的方法 Collection的方法List都继承了&#xff0c;可以使用Collection中的方法 此外&#xff0c;List集合因为有索引&#xff0c;…...

使用谱聚类将相似度矩阵分为2类

使用谱聚类将相似度矩阵分为2类的步骤如下&#xff1a; 构建相似度矩阵&#xff1a;提供的1717矩阵已满足对称性且对角线为1。 计算度矩阵&#xff1a;对每一行求和得到各节点的度&#xff0c;形成对角矩阵。 计算归一化拉普拉斯矩阵&#xff1a;采用对称归一化形式 LsymI−D…...

【Bootstrap V4系列】学习入门教程之 组件-表单(Forms)高级用法(二)

Bootstrap V4系列 学习入门教程之 组件-表单&#xff08;Forms&#xff09;高级用法&#xff08;二&#xff09; 表单&#xff08;Forms&#xff09;高级用法&#xff08;二&#xff09;一、Help text 帮助文本二、Disabled forms 禁用表单三、Validation 验证3.1 How it works…...

【许可证】Open Source Licenses

长期更新 扩展&#xff1a;shield.io装饰 开源许可证&#xff08;Open Source Licenses&#xff09;有很多种&#xff0c;每种都有不同的授权和限制&#xff0c;适用于不同目的。 默认的ISC&#x1f7f0;MIT License是否可商用是否要求开源衍生项目是否必须署名是否有专利授权…...

RT-Thread 深入系列 Part 7:RT-Thread vs 其他 RTOS 对比与选型指南

摘要 本文对比 RT-Thread、FreeRTOS、Zephyr、uC/OS、Mbed OS 等主流嵌入式操作系统,从功能特性、社区生态、学习成本、商业支持、安全性和典型应用场景六个维度进行详尽对比,并给出不同项目类型下的选型建议,帮助你为下一个嵌入式项目做出最合适的决策。 目录 功能特性对比…...

每天五分钟机器学习:KTT条件

本文重点 在前面的课程中,我们学习了拉格朗日乘数法求解等式约束下函数极值,如果约束不是等式而是不等式呢?此时就需要KTT条件出手了,KTT条件是拉格朗日乘数法的推广。KTT条件不仅统一了等式约束与不等式约束的优化问题求解范式,KTT条件给出了这类问题取得极值的一阶必要…...

金融学知识笔记

金融学知识笔记 一、引言 金融学它结合了数学、概率论、统计学、经济学和计算机科学等多学科的知识&#xff0c;用于解决金融领域中的各种问题&#xff0c;如金融衍生品定价、投资组合优化、风险管理和固定收益证券分析等。通过对金融学的学习&#xff0c;我们可以更好地理解…...

Web3 实战项目项目部署到 GitHub 和上线预览的完整指南

目录 &#x1f680; 一、部署到 GitHub ✅ 前置准备 &#x1f9f1; 部署步骤&#xff1a; 1. 创建一个 GitHub 仓库 2. 上传项目文件 方法一&#xff1a;使用 Git 命令行 方法二&#xff1a;直接上传 &#x1f310; 二、通过 GitHub Pages 免费上线 DApp&#xff08;前端…...

嵌入式开发学习(阶段二 C语言基础)

C语言&#xff1a;第05天笔记 内容提要 分支结构 条件判断用if语句实现分支结构用switch语句实现分支结构 分支结构 条件判断 条件判断&#xff1a;根据某个条件成立与否&#xff0c;决定是否执行指定的操作。 条件判断的结果是逻辑值&#xff0c;也就是布尔类型值&#…...

【沉浸式求职学习day35】【Tomcat安装、配置】【Http简述】

&#x1f629;&#x1f629;&#x1f629;&#xff0c;真的每天忙死&#xff0c;感觉自己精神上压力真的很大&#xff0c;希望我的努力能够激励到你们~ 沉浸式求职学习 Tomcat1.安装2.Tomcat启动和配置3.配置高难度面试题 4.发布一个web网站5.Http1.什么是HTTP2.两个时代3.Htt…...

基于大模型的新型隐球菌脑膜炎智能诊疗全流程系统设计与实现的技术方案文档

目录 一、术前风险预测系统1. 多模态融合模型架构2. 风险预测流程图(Mermaid)二、麻醉剂量预测系统1. 靶控输注(TCI)模型2. 麻醉方案优化流程图(Mermaid)三、术后并发症预测模型1. 时序预测模型(LSTM)2. 并发症预测流程图(Mermaid)四、健康教育管理模块1. 移动健康(…...

如何实现PLC远程编程

1. 什么是PLC编程 PLC编程是指为可编程逻辑控制器&#xff08;Programmable Logic Controller&#xff0c;PLC&#xff09;编写控制逻辑的过程。PLC是一种工业自动化控制设备&#xff0c;广泛应用于制造业、机械控制、流水线、电力系统等领域&#xff0c;用于替代传统的继电器…...

Go基于plugin的热更新初体验

背景 对于一个部署在生产环境的项目来说&#xff0c;我们希望当代码出现bug的时候&#xff0c;可以不用重启进程而达到动态修改代码的目的—— 这就是代码热部署&#xff01; 使用java做游戏服务器&#xff0c;最大的好处是&#xff0c;当代码出现bug&#xff0c;可以直接热…...

【STM32 学习笔记】I2C通信协议

注&#xff1a;通信协议的设计背景 3:00~10:13 I2C 通讯协议(Inter&#xff0d;Integrated Circuit)是由Phiilps公司开发的&#xff0c;由于它引脚少&#xff0c;硬件实现简单&#xff0c;可扩展性强&#xff0c; 不需要USART、CAN等通讯协议的外部收发设备&#xff0c;现在被广…...

RAG与语义搜索:让大模型成为测试工程师的智能助手

引言 AI大模型风头正劲&#xff0c;自动生成和理解文本的能力让无数行业焕发新生。测试工程师也不例外——谁不想让AI自动“看懂需求、理解接口、生成用例”&#xff1f;然而&#xff0c;很多人发现&#xff1a;直接丢问题给大模型&#xff0c;答案貌似“懂行”&#xff0c;细…...

Nakama:让游戏与应用更具互动性和即时性

在现代游戏和应用程序开发中&#xff0c;实现社交互动和实时功能已成为用户体验的核心需求。为满足这种需求&#xff0c;许多开发者正转向分布式服务器技术&#xff0c;在这些技术中&#xff0c;Nakama 构建起了一座桥梁。Nakama 是一个开源的分布式服务器&#xff0c;专门为社…...

[Spring AOP 7] 动态通知调用

动态通知调用 本笔记基于黑马程序员 Spring高级源码解读 更美观清晰的版本在&#xff1a;Github 注意&#xff1a;阅读本章内容之前&#xff0c;建议先熟悉静态通知调用的内容 在静态通知调用一节中&#xff0c;我们还有一个悬而未决的问题。 我们谈到了调用链MethodInvocation…...

Redis 集群

集群基本介绍 广义的集群&#xff0c;只要是多个机器构成了分布式系统&#xff0c;都可以称为是一个“集群”&#xff08;主从模式&#xff0c;哨兵模式&#xff09; 狭义的集群&#xff0c;Redis 提供的集群模式&#xff0c;在这个模式下主要解决的是存储空间不足的问题&…...

【Redis】基础命令数据结构

文章目录 基础命令keysexistsdelexpirettltype 数据结构和内部编码 在介绍数据类型前先介绍一下 Redis 的基础命令&#xff0c;方便理解 基础命令 keys 返回所有满足样式&#xff08;pattern&#xff09;的 key keys pattern 当前有如下 key PS&#xff1a;实际开发环境和生…...

C++(6):逻辑运算符

目录 1. 代码示例 示例 1&#xff1a;基础用法 示例 2&#xff1a;条件判断 2. 短路求值&#xff08;Short-Circuit Evaluation&#xff09; 代码示例 3. 实际应用场景 场景 1&#xff1a;输入合法性验证 场景 2&#xff1a;游戏状态判断 4. 注意事项 逻辑运算符用于组…...

项目管理从专家到小白

敏捷开发 Scrum 符合敏捷开发原则的一种典型且在全球使用最为广泛的框架。 三个角色 产品负责人Product Ower&#xff1a;专注于了解业务、客户和市场要求&#xff0c;然后相应地确定工程团队需要完成的工作的优先顺序。 敏捷教练Scrum Master&#xff1a;确保 Scrum 流程顺…...

AI绘画灵感觉醒指南:从灵感源泉到创意输出

目录 一、引言 二、灵感来源 2.1 现实生活 2.2 其他艺术作品 2.3 文学作品 三、灵感转化为输入提示 3.1 明确主题与核心元素 3.2 细化描述 3.3 使用修饰词与专业术语 3.4 组合与优化提示词 四、案例分析 4.1 从现实生活获取灵感 4.2 从其他艺术作品获取灵感 4.3 …...

【Java学习】枚举(匿名类详解)

目录 一、匿名类 1.形式 2.性质 2.1匿名性 2.1.1同步性 使用场景 2.1.2复用性 2.1.3向上转型 2.2实现性 3.传参 3.1构造传全参 3.1.1过程 3.1.2效果 2.1.4原子类构造无参 4.权限 二、枚举类 1.枚举常量 2.性质 2.1多态性 2.2单例性 2.2.1private保护 2.2…...

力扣题解:2、两数相加

个人认为&#xff0c;该题目可以看作合并两个链表的变种题&#xff0c;本题与21题不同的是&#xff0c;再处理两个结点时&#xff0c;对比的不是两者的大小&#xff0c;而是两者和是否大于10&#xff0c;加法计算中大于10要进位&#xff0c;所以我们需要声明一个用来标记是否进…...