底层源码和具体测试解析HotSpot JVM的notify唤醒有序性(5000字详解)
在大家的认知里,或者大家也可能搜过,notify唤醒机制到底是随机的呢?还是顺序的呢?在网上其实也有很多人说notify的唤醒机制就是随机的,但实际上并不是这样的,notify的唤醒机制是先进先出的!
目录
源码注释说明
具体测试用例
测试用例一
测试用例二
底层原理解析
ObjectMonitor 的数据结构
wait源码
notify源码
DequeueWaiter源码
源码结论
源码注释说明
让我们先来看看源码注释是怎么写的
Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.
翻译为:
唤醒正在等待此对象的监视器的单个线程。如果任何线程正在等待此对象,则选择其中一个线程进行唤醒。选择是任意的,由实现自行决定。线程通过调用其中一个 wait 方法等待对象的监视器。
在注释中其实就已经说明了,选择是任意的,由实现自行决定的,既然注释都这样写了,那我们就先用测试用例测试一下。
具体测试用例
测试用例一
public class Main {private static final Object lock = new Object();private static List<String> awakingOrder = Collections.synchronizedList(new ArrayList<>());private static final int THREAD_COUNT = 100;private static CountDownLatch readyLatch = new CountDownLatch(THREAD_COUNT);private static CountDownLatch startLatch = new CountDownLatch(1);private static CountDownLatch finishLatch = new CountDownLatch(THREAD_COUNT);static class WaitingThread extends Thread {private String name;public WaitingThread(String name) {this.name = name;}@Overridepublic void run() {try {// 先通知主线程该线程已准备就绪readyLatch.countDown();// 等待主线程发出开始信号startLatch.await();synchronized (lock) {System.out.println(name + " 准备等待");lock.wait();awakingOrder.add(name);System.out.println(name + " 被唤醒");}} catch (InterruptedException e) {e.printStackTrace();} finally {finishLatch.countDown();}}}public static void main(String[] args) throws InterruptedException {List<WaitingThread> threads = new ArrayList<>();// 创建并启动100个线程for (int i = 1; i <= THREAD_COUNT; i++) {WaitingThread thread = new WaitingThread("线程" + i);threads.add(thread);thread.start();}// 等待所有线程准备就绪readyLatch.await();System.out.println("所有线程已准备就绪");// 发出开始信号,让所有线程开始等待startLatch.countDown();// 确保所有线程都进入等待状态Thread.sleep(1000);// 依次唤醒所有线程for (int i = 0; i < THREAD_COUNT; i++) {synchronized (lock) {System.out.println("开始第 " + (i + 1) + " 次唤醒");lock.notify();}// 给一点时间让被唤醒的线程执行完毕Thread.sleep(10);}// 等待所有线程执行完毕finishLatch.await();// 输出唤醒顺序System.out.println("唤醒顺序: " + awakingOrder);System.out.println("唤醒的线程总数: " + awakingOrder.size());}
}
测试用例代码如上,我一开始看的时候还在想HotSpot JVM的的notify唤醒机制原来真的是顺序唤醒的,但是我再仔细看的时候发现并不是这样的!
当我再往上看的时候发现,原来因为线程并发执行的原因导致并不是按照我原先设置的顺序获取到了synchronized锁!而导致了这个原因,经过对比其实可以发现,线程获取到锁等待的顺序是和被唤醒的顺序是一致的!所以HotSpot JVM的notify实现是先进先出的有序队列。
测试用例二
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;public class Main {private static final Object lock = new Object();private static List<String> awakingOrder = Collections.synchronizedList(new ArrayList<>());private static final int THREAD_COUNT = 100;private static volatile int currentThreadIndex = 0;static class WaitingThread extends Thread {private String name;private int index;private CountDownLatch prevLatch;private CountDownLatch myLatch;public WaitingThread(String name, int index, CountDownLatch prevLatch, CountDownLatch myLatch) {this.name = name;this.index = index;this.prevLatch = prevLatch;this.myLatch = myLatch;}@Overridepublic void run() {try {// 等待前一个线程就绪if (prevLatch != null) {prevLatch.await();}synchronized (lock) {System.out.println(name + " 准备等待,序号: " + index);// 通知下一个线程可以开始等待if (myLatch != null) {myLatch.countDown();}lock.wait();awakingOrder.add(name);System.out.println(name + " 被唤醒,序号: " + index);}} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) throws InterruptedException {List<WaitingThread> threads = new ArrayList<>();List<CountDownLatch> latches = new ArrayList<>();// 创建用于线程间同步的CountDownLatch数组for (int i = 0; i < THREAD_COUNT; i++) {latches.add(new CountDownLatch(1));}// 创建并启动线程for (int i = 0; i < THREAD_COUNT; i++) {CountDownLatch prevLatch = i == 0 ? null : latches.get(i - 1);CountDownLatch myLatch = latches.get(i);WaitingThread thread = new WaitingThread("线程" + (i + 1), i + 1, prevLatch, myLatch);threads.add(thread);thread.start();}// 等待一段时间确保所有线程都进入等待状态Thread.sleep(2000);System.out.println("开始按顺序唤醒线程");// 按顺序唤醒线程for (int i = 0; i < THREAD_COUNT; i++) {synchronized (lock) {System.out.println("准备唤醒第 " + (i + 1) + " 个线程");lock.notify();}// 给足够的时间让被唤醒的线程完成执行Thread.sleep(50);}// 等待所有线程完成for (Thread thread : threads) {thread.join();}// 输出唤醒顺序System.out.println("唤醒顺序: " + awakingOrder);System.out.println("唤醒的线程总数: " + awakingOrder.size());// 验证唤醒顺序是否正确boolean isOrdered = true;for (int i = 0; i < awakingOrder.size(); i++) {String expectedThread = "线程" + (i + 1);if (!awakingOrder.get(i).equals(expectedThread)) {isOrdered = false;break;}}System.out.println("唤醒顺序是否正确: " + isOrdered);}
}
修改原来的代码 ,为每个线程创建一个 CountDownLatch,每个线程需要等待前一个线程进入等待状态才能继续,使用 prevLatch 和 myLatch 确保线程按顺序进入等待状态,这样就可以顺序的进入锁了,让我们再看下运行结果
这样其实就可以清晰的看到这个唤醒的结果了,就是先进先出,顺序!
底层原理解析
现在让我们深入分析 HotSpot JVM 中的实现原理
ObjectMonitor 的数据结构
ObjectMonitor 的数据结构在 HotSpot 中,synchronized 的实现依赖于 ObjectMonitor 类,其结构如下:
ObjectMonitor() {_header = NULL;_count = 0;_waiters = 0;_recursions = 0;_object = NULL;_owner = NULL;_WaitSet = NULL; // 等待线程队列_WaitSetLock = 0;_Responsible = NULL;_succ = NULL;_cxq = NULL;FreeNext = NULL;_EntryList = NULL; // 待竞争线程队列_SpinFreq = 0;_SpinClock = 0;
}
wait源码
让我们看看具体的wait等待实现
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {// 将当前线程封装成 ObjectWaiter 对象ObjectWaiter node(THREAD);node.TState = ObjectWaiter::TS_WAIT;// 加入等待队列AddWaiter(&node);// 释放对象锁exit(true, THREAD);// 等待被唤醒Thread::SpinWait(node);
}
notify源码
再看看notify的唤醒实现
void ObjectMonitor::notify(TRAPS) {// DequeueWaiter 方法会从 _WaitSet 队列头部取出一个等待线程ObjectWaiter* waiter = DequeueWaiter();if (waiter != NULL) {// 将等待线程移动到 EntryListExitExit(waiter);}
}
DequeueWaiter源码
ObjectWaiter* ObjectMonitor::DequeueWaiter() {// 从 _WaitSet 队列头部获取第一个等待线程ObjectWaiter* waiter = _WaitSet;if (waiter != NULL) {_WaitSet = waiter->_next;waiter->_prev = NULL;waiter->_next = NULL;}return waiter;
}
源码结论
从源码可以看出,wait() 的线程被放入 WaitSet 队列,notify() 总是唤醒 WaitSet 队列中的第一个线程,虽然 Java 规范说 notify 的选择是随机的,但在 HotSpot 实现中实际上是 FIFO(先进先出)的
运行上面的测试代码,你也会发现,线程被唤醒的顺序与它们进入等待状态的顺序是一致的,多次运行结果都是一样的(在相同的 JVM 实现下),这验证了在 HotSpot JVM 中,notify() 确实是按照 FIFO 顺序唤醒线程的
但需要注意的是:这个行为是 HotSpot JVM 的具体实现,其他 JVM 可能会有不同的实现
相关文章:
底层源码和具体测试解析HotSpot JVM的notify唤醒有序性(5000字详解)
在大家的认知里,或者大家也可能搜过,notify唤醒机制到底是随机的呢?还是顺序的呢?在网上其实也有很多人说notify的唤醒机制就是随机的,但实际上并不是这样的,notify的唤醒机制是先进先出的! 目…...
Jenkins(CI/CD工具)
1. 什么是 Jenkins? Jenkins 是一个开源的持续集成(CI)和持续交付/部署(CD)工具,用于自动化软件构建、测试和部署过程。 2. Jenkins 优势 (1)开源免费:社区活跃&#…...
Apache Sqoop数据采集问题
Sqoop数据采集格式问题 一、Sqoop工作原理二、Sqoop命令格式三、Oracle数据采集格式问题四、Sqoop增量采集方案 Apache Sqoop是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysql、postgresql…)间进行数据的传递,可以将一个关系型数据库&…...
再学GPIO(二)
GPIO寄存器 每个GPI/O端口有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。 GPIO…...
err: Error: Request failed with status code 400
好的,今天学习ai的时候从前端发送请求,实在是想不通为啥会啥是一个坏请求,后来从前端方法一个一个找参数,传递的值都有,然后想到我这边需要传递的是一个对象,那么后端使用的RequestParam就接收不到json对象…...
解决qnn htp 后端不支持boolean 数据类型的方法。
一、背景 1.1 问题原因 Qnn 模型在使用fp16的模型转换不支持类型是boolean的cast 算子,因为 htp 后端支持量化数据类型或者fp16,不支持boolean 类型。 ${QNN_SDK_ROOT_27}/bin/x86_64-linux-clang/qnn-model-lib-generator -c ./bge_small_fp16.cpp -b …...
k8s学习记录(五):Pod亲和性详解
一、前言 上一篇文章初步探讨了 Kubernetes 的节点亲和性,了解到它在 Pod 调度上比传统方式更灵活高效。今天我们继续讨论亲和性同时Kubernetes 的调度机制。 二、Pod亲和性 上一篇文章中我们介绍了节点亲和性,今天我们讲解一下Pod亲和性。首先我们先看…...
MongoDB与PHP7的集成与优化
MongoDB与PHP7的集成与优化 引言 随着互联网技术的飞速发展,数据库技术在现代软件开发中扮演着越来越重要的角色。MongoDB作为一种流行的NoSQL数据库,以其灵活的数据模型和强大的扩展性受到众多开发者的青睐。PHP7作为当前最流行的服务器端脚本语言之一,其性能和稳定性也得…...
maven相关概念深入介绍
1. pom.xml文件 就像Make的MakeFile、Ant的build.xml一样,Maven项目的核心是pom.xml。POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。…...
以科技之力,启智慧出行 —— 阅读《NVIDIA 自动驾驶安全报告》及观看实验室视频有感
作为中南民族大学通信工程专业的学生,近期研读《NVIDIA 自动驾驶安全报告》并观看其实验室系列视频后,我深刻感受到自动驾驶技术不仅是一场交通革命,更是一次社会生产力的解放与民族精神的升华。这场变革的浪潮中,我看到了科技如何…...
2P4M-ASEMI机器人功率器件专用2P4M
编辑:LL 2P4M-ASEMI机器人功率器件专用2P4M 型号:2P4M 品牌:ASEMI 封装:TO-126 批号:最新 引脚数量:3 封装尺寸:如图 特性:双向可控硅 工作结温:-40℃~150℃ 在…...
基础的贝叶斯神经网络(BNN)回归
下面是一个最基础的贝叶斯神经网络(BNN)回归示例,采用PyTorch实现,适合入门理解。 这个例子用BNN拟合 y x 噪声 的一维回归问题,输出均值和不确定性(方差)。 import torch import torch.nn a…...
小黑享受思考心流: 73. 矩阵置零
小黑代码 class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""items []m len(matrix)n len(matrix[0])for i in range(m):for j in range(n):if not m…...
整合 | 大模型时代:微调技术在医疗智能问答矩阵的实战应用20250427
🔎 整合 | 大模型时代:微调技术在医疗智能问答矩阵的实战应用 一、引言 在大模型技术高速变革的背景下,数据与微调技术不再是附属品,而是成为了AI能力深度重构的核心资产。 尤其在医疗行业中,微调技术改写了智能分诊和…...
Web安全:威胁解析与综合防护体系构建
Web安全:威胁解析与综合防护体系构建 Web安全是保护网站、应用程序及用户数据免受恶意攻击的核心领域。随着数字化转型加速,攻击手段日益复杂,防护需兼顾技术深度与系统性。以下从威胁分类、防护技术、最佳实践及未来趋势四个维度࿰…...
spring项目rabbitmq es项目启动命令
应该很多开发者遇到过需要启动中间件的情况,什么测试服务器挂了,服务连不上nacos了巴拉巴拉的,虽然是测试环境,但也会手忙脚乱,疯狂百度。 这里介绍一些实用方法 有各种不同的场景,一是重启,服…...
人工智能期末复习1
该笔记为2024.7出版的人工智能技术应用导论(第二版)课本部分的理论总结。 一、人工智能的产生与发展 概念:人工智能是通过计算机系统和模型模拟、延申和拓展人类智能的理论、方法、技术及应用系统的一门新的技术科学。 发展:19…...
深入理解指针(5)
字符指针变量 对下述代码进行调试 继续go,并且观察p2 弹出错误: 为什么报错呢? 因为常量字符串是不能被修改的,否则,编译器报错。 最后,打印一下: 《剑指offer》中收录了⼀道和字符串相关的笔试题&#…...
新魔百和CM311-5_CH/YST/ZG代工_GK6323V100C_2+8G蓝牙版_强刷卡刷固件包(可救砖)
新魔百和CM311-5_CH/YST/ZG代工_GK6323V100C_28G蓝牙版_强刷卡刷固件包(可救砖) 1、准备一个优盘卡刷强刷刷机,用一个usb2.0的8G以下U盘,fat32,2048块单分区格式化(强刷对ÿ…...
磁盘清理git gc
#!/bin/bash find / -type d -name “.git” 2>/dev/null | while read -r git_dir; do repo_dir ( d i r n a m e " (dirname " (dirname"git_dir") echo “Optimizing r e p o d i r " c d " repo_dir" cd " repodir"cd&…...
django admin AttributeError: ‘UserResorce‘ object has no attribute ‘ID‘
在 Django 中遇到 AttributeError: ‘UserResource’ object has no attribute ‘ID’ 这类错误通常是因为你在代码中尝试访问一个不存在的属性。在你的例子中,错误提示表明 UserResource 类中没有名为 ID 的属性。这可能是由以下几个原因造成的: 拼写错…...
现代Python打包工具链
现代Python打包工具如Poetry、Flit和Hatch提供了更简单、更强大的方式来管理项目依赖和打包流程。下面我将通过具体示例详细介绍这三种工具。 1. Poetry - 全功能依赖管理工具 Poetry是最流行的现代Python项目管理工具之一,它集依赖管理、虚拟环境管理和打包发布于一…...
(done) 吴恩达版提示词工程 8. 聊天机器人 (聊天格式设计,上下文内容,点餐机器人)
视频:https://www.bilibili.com/video/BV1Z14y1Z7LJ/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 别人的笔记:https://zhuanlan.zhihu.com/p/626966526 8. 聊天机器人(Chatbot) …...
Maven概述
1.maven是什么? Maven 是一个基于项目对象模型(Project Object Model,POM)概念的项目构建工具,主要用于 Java 项目的构建、依赖管理和项目信息管理。(跨平台的项目管理工具,用于构建和管理任何…...
SKLearn - Biclustering
文章目录 Biclustering (双聚类)谱二分聚类算法演示生成样本数据拟合 SpectralBiclustering绘制结果 Spectral Co-Clustering 算法演示使用光谱协同聚类算法进行文档的二分聚类 Biclustering (双聚类) 关于双聚类技术的示例。 谱…...
使用c++实现一个简易的量子计算,并向外提供服务
实现一个简易的量子计算模拟器并提供服务是一个相对复杂的过程,涉及到量子计算的基本概念、C编程以及网络服务的搭建。以下是一个简化的步骤指南,帮助你开始这个项目: 步骤 1: 理解量子计算基础 在开始编码之前,你需要对量子计算…...
京东攻防岗位春招面试题
围绕电商场景,以下是5道具有代表性的技术面试题及其解析,覆盖供应链、电商大促、红蓝对抗等场景。 《网安面试指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇网安资料库https://mp.weixin.qq.com/s?…...
Kafka批量消费部分处理成功时的手动提交方案
Kafka批量消费部分处理成功时的手动提交方案 当使用Kafka批量消费时,如果500条消息中只有部分处理成功,需要谨慎处理偏移量提交以避免消息丢失或重复消费。以下是几种处理方案示例: 方案1:记录成功消息并提交最后成功偏移量 Co…...
消息中间件
零、文章目录 消息中间件 1、中间件 (1)概述 中间件(Middleware)是位于操作系统、网络与数据库之上,应用软件之下的一层独立软件或服务程序,其核心作用是连接不同系统、屏蔽底层差异,并为应…...
vue3直接操作微信小程序云开发数据库,web网页对云数据库进行增删改查
我们开发好小程序以后,有时候需要编写一个管理后台网页对数据库进行管理,之前我们只能借助云开发自带的cms网页,但是cms网页设计的比较丑,工作量和代码量也不够,所以我们今天就来带大家实现用vue3编写管理后台直接管理…...
重塑编程体验边界:明基RD280U显示器深度体验
重塑编程体验边界:明基RD280U显示器深度体验 写在前面 本文将以明基RD280U为核心,通过技术解析、实战体验与创新案例,揭示专业显示器如何重构开发者的数字工作台。 前言:当像素成为生产力的催化剂 在GitHub的年度开发者调查中&…...
Linux命令-iostat
iostat 命令介绍 iostat 是一个用于监控 Linux 系统输入/输出设备加载情况的工具。它可以显示 CPU 的使用情况以及设备和分区的输入/输出统计信息,对于诊断系统性能瓶颈(如磁盘或网络活动缓慢)特别有用。 语法: iostat [options…...
Hyper-V安装Win10系统,报错“No operating system was loaded“
环境: Win10专业版 Hyper-V 问题描述: Hyper-V安装Win10系统,报错"No operating system was loaded" 已挂载ISO但仍无法启动的深度解决方案 🔧如果已确认ISO正确挂载且启动顺序已调整,但虚拟机仍提…...
Zabbix
zabbix官网: https://www.zabbix.com zabbix中文操作手册:https://www.zabbix.com/documentation/5.0/zh/manual/introduction/features 1、SERVER Zabbix server 是 Zabbix 软件的核心组件。Zabbix Agent 向Zabbix server报告可用性、系统完整性信息和统计信息。…...
NEPCON China 2025 | 具身智能时代来临,灵途科技助力人形机器人“感知升级”
4月22日至24日,生产设备暨微电子工业展(NEPCON China 2025)在上海如期开展。本届展会重磅推出“人形机器人拆解展区”,汇聚35家具身智能产业链领军企业,围绕机械结构、传感器布局、驱动系统与AI算法的落地应用…...
css响应式布局设置子元素高度和宽度一样
css响应式布局设置子元素高度和宽度一样 常常遇到响应式布局 其中父元素(类名为.list)包含多个子元素(类名为.item),每个子元素中显示一张图片,并且这些图片能够根据子元素的宽度和高度进行自适应调整。 …...
【AI论文】RefVNLI:迈向可扩展的主题驱动文本到图像生成评估
摘要:主题驱动的文本到图像(T2I)生成旨在生成与给定文本描述一致的图像,同时保留参考主题图像的视觉特征。 尽管该领域具有广泛的下游适用性——从增强图像生成的个性化到视频渲染中一致的角色表示——但该领域的进展受到缺乏可靠…...
信创系统 sudoers 权限配置实战!从小白到高手
好文链接:实战!银河麒麟 KYSEC 安全中心执行控制高级配置指南 Hello,大家好啊!今天给大家带来一篇关于信创终端操作系统中 sudoers 文件详解的实用文章!在 Linux 系统中,sudo 是一项非常重要的权限控制机制…...
用户行为检测技术解析:从请求头到流量模式的对抗与防御
用户行为检测是反爬机制的核心环节,网站通过分析请求特征、交互轨迹和时间模式,识别异常流量并阻断爬虫。本文从基础特征检测与高级策略分析两个维度,深入解析用户行为检测的技术原理与对抗方案。 一、基础特征检测:请求头与交互…...
关于Android Studio的AndroidManifest.xml的详解
AndroidManifest.xml 是 Android 项目的核心配置文件,它定义了应用的基本信息、所需权限、组件、功能等。它为 Android 系统提供了关于应用如何运行的重要信息。每个 Android 应用程序必须包含这个文件,而且这个文件的配置直接影响到应用的行为和安装要求…...
全栈自动化:从零构建智能CI/CD流水线
1. 基础架构:GitLab Kubernetes 1.1 GitLab CI/CD核心配置 GitLab通过.gitlab-ci.yml定义流水线阶段。以下是一个基础模板: stages:- build- test- deploybuild_job:stage: buildscript:- echo "Compiling the code..."- make…...
xe-upload上传文件插件
1.xe-upload地址:文件选择、文件上传组件(图片,视频,文件等) - DCloud 插件市场 2.由于开发app要用到上传文件组件,uni.chooseFile在app上不兼容,所以找到了xe-upload,兼容性很强&a…...
PySpark中DataFrame应用升阶及UDF使用
目录 1. 加载数据2. 列常见操作2.1 添加新列2.2 重命名列2.3 删除指定列2.4 修改数据 3 空值处理3.1 丢弃空值3.2 空值填充 4 聚合操作4.1 分组聚合 5 用户自定义函数(UDF)5.1 传统UDF函数5.2 Pandas UDF(向量化UDF) 参考资料 imp…...
C++ ——引用
引用定义 引用是一个已存在的变量的别名。 用法 类型 & 别名 引用指向的变量名 关于别名的理解: 别名可以理解为绰号或者小名,比如美猴王、齐天大圣、斗战胜佛等,指的都是孙悟空。 这意味着: ①别名和别名指向的变量其实是同…...
OpenCV 图形API(65)图像结构分析和形状描述符------拟合二维点集的直线函数 fitLine2D()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 拟合一条直线到2D点集。 该函数通过最小化 ∑ i ρ ( r i ) \sum_i \rho(r_i) ∑iρ(ri)来将一条直线拟合到2D点集,其中 ri 是第…...
k8s生成StarRocks集群模版
集群由1个fe3个be组成,满足以下要求: 1、由3个pod组成,每pod分配2c4g 2、第一个pod里有一个be与一个fe,同在一个容器里,fe配置jvm内存设置为1024mb,be的jvm内存设置为1024MB 3、第二第三个pod里分别有一…...
web基础+HTTP+HTML+apache
目录 一.web基础 1.1web是什么 1.2HTTP 1.2.1HTTP的定义 1.2.2 HTTP请求过程 1.2.3 HTTP报文 1 请求报文 2 响应报文 1.2.4 HTTP协议状态码 1.2.5 HTTP方法 1.2.6 HTTP协议版本 二.HTML CSS和JavaScript 2.1HTML 2.1.1HTML的概述 2.1.2 HTML中的部分基本标签&…...
C++修炼:list模拟实现
Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路! 我的博客:<但凡. 我的专栏:《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞,关注&am…...
Lua 第12部分 日期和时间
Lua 语言的标准库提供了两个用于操作日期和时间的函数,这两个函数在 C 语言标准库中也存在,提供的是同样的功能。 虽然这两个函数看上去很简单,但依旧可以基于这些简单的功能完成很多复杂的工作。 Lua 语言针对日期和时间使用两种表示方式。 …...
NL2SQL调研
一 背景 1.1 引言 随着数据时代的到来,数据库已成为企业和组织存储、管理和分析数据的核心基础设施。然而,传统的数据库查询需要使用结构化查询语言(SQL),这要求用户具备特定的技术知识,限制了数据库的广…...