TinyWebSever项目面试题整理
TinyWebSever项目面试题整理
1.为什么要做这样一个项目?
-
满足高并发和高性能需求:现代Web应用面对大量用户,Web服务器需要高效处理并发连接。比如通过线程池、非阻塞I/O、事件驱动机制(如epoll),Web服务器可以有效管理成千上万的并发请求,确保服务不会因高流量而崩溃或变慢。
-
理解网络编程:通过使用线程池、非阻塞socket、epoll等技术,项目可以帮助熟悉Linux下的网络编程模型,深入理解如何处理并发连接、如何进行事件驱动的网络通信等核心技术。
-
实践HTTP协议和Web服务器:构建一个能够解析HTTP请求并进行响应的Web服务器,有助于理解HTTP协议的工作原理,学会如何处理GET和POST请求,增强对Web服务端架构的理解。
-
提升系统优化意识:通过进行性能测试和优化(例如使用Webbench测试并发性能),这个项目还帮助你了解系统性能瓶颈、提升程序的效率、理解并实现高效的并发模型。
线程池相关
2.手写一下线程池
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>
#include <atomic>class ThreadPool {
public:// 构造函数:创建线程并启动线程池ThreadPool(size_t threads) : stop(false) {for (size_t i = 0; i < threads; ++i) {workers.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(this->queue_mutex);// 等待任务队列中有任务,或者停止信号this->condition.wait(lock, [this] {return this->stop || !this->tasks.empty();});if (this->stop && this->tasks.empty()) {return; // 线程退出}// 从任务队列中取出一个任务task = std::move(this->tasks.front());this->tasks.pop();}// 执行任务task();}});}}// 向线程池添加新任务template <class F, class... Args>auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;// 打包任务,保存到shared_ptr中auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queue_mutex);// 禁止在停止线程池后添加任务if (stop) {throw std::runtime_error("enqueue on stopped ThreadPool");}// 将任务添加到队列tasks.emplace([task]() { (*task)(); });}// 通知一个线程有任务可以执行condition.notify_one();return res;}// 析构函数:等待所有线程完成任务后关闭~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for (std::thread& worker : workers) {worker.join(); // 等待线程结束}}private:std::vector<std::thread> workers; // 工作线程std::queue<std::function<void()>> tasks; // 任务队列std::mutex queue_mutex; // 任务队列互斥锁std::condition_variable condition; // 条件变量,用于任务调度std::atomic<bool> stop; // 停止标志
};int main() {// 创建一个线程池,包含4个工作线程ThreadPool pool(4);// 向线程池添加一些任务并获取结果auto result1 = pool.enqueue([] { return "Hello, "; });auto result2 = pool.enqueue([](const std::string& name) { return name + "World!"; }, "C++ ");// 输出任务结果std::cout << result1.get() << result2.get() << std::endl;return 0;
}
3. 线程的同步机制有哪些?
线程同步机制用于在多线程环境中协调线程的执行,避免数据竞争和资源冲突。常见的线程同步机制有以下几种:
1. 互斥锁(Mutex)
场景: 当多个线程需要安全地修改共享数据时,比如线程要同时操作一个变量、写日志、或者修改数据结构。
特点: 互斥锁就像一个房间的钥匙,只有拿到钥匙的人能进房间修改里面的东西,别人必须等他出来并把钥匙还回去。这种方式确保只有一个线程在修改资源,其他线程必须排队。
适合场景: 适用于简单的“谁进了房间谁就不能让别人进”的场景,比如银行柜台,只有一个人能处理事情,其他人要排队。
2. 读写锁(Read-Write Lock)
场景: 当多个线程需要读取数据,但只有少数线程需要写数据时,比如你有一个资源,大多数线程只是查看它,只有少数线程需要修改它。
特点: 读写锁像图书馆:多个读者(线程)可以同时看书(读取数据),但是一旦有人需要改书(写数据),其他读者必须等这个人改完再看。这种机制允许并发读,减少锁的开销。
写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)
适合场景: 适用于“读多写少”的场景,比如在线图书馆,很多人看书,但只有管理员会更新书。
3. 条件变量(Condition Variable)
场景: 适用于某个线程必须等一个条件满足后才能继续工作,比如一个线程在等另一个线程完成任务
。
特点: 条件变量就像开会时等老板发言的场景。线程们在“会议室”里等条件满足(比如任务队列有新任务),一旦条件满足了,其他线程被通知并开始工作。
适合场景: 用于生产者-消费者模型,比如有一个任务队列,消费者线程只有在有新任务时才能干活。
4. 信号量(Semaphore)
场景: 控制资源的访问数量,比如限制某个资源同时只能有固定数量的线程使用(比如数据库连接池)。
特点: 信号量就像停车场的闸机,只允许有限数量的车(线程)进入。停车场满了,其他车只能在外面等。有时可以允许多个线程同时访问,但有上限。
适合场景: 适用于控制资源访问数量的场景,比如一个数据库有5个连接池,你只允许最多5个线程同时连接数据库。
5. 自旋锁(Spinlock)
场景: 当锁的等待时间非常短时,比如线程很快就能拿到锁的情况。
特点: 自旋锁像排队等公交车,如果你知道车马上就到,你可能就会站着等,而不会坐下休息(线程不会睡眠,而是不断检查锁是否可用)。它适用于等待时间非常短的场景,因为忙等很浪费资源。
适合场景: 适合非常短时间的临界区,适合多核处理器上多个线程竞争时,开销低于传统的互斥锁。
每种机制都有特定的使用场景和特点,合理选择可以避免资源争用和死锁,确保程序高效运行。
4. 线程池中的工作线程是一直等待吗?
在run函数中,我们为了能够处理高并发的问题,将线程池中的工作线程都设置为阻塞等待在请求队列是否不为空的条件上,因此项目中线程池中的工作线程是处于 一直阻塞等待 的模式下的。
5. 你的线程池工作线程处理完一个任务后的状态是什么?
(1) 当处理完任务后如果请求队列为空时,则这个线程重新回到阻塞等待的状态
(2) 当处理完任务后如果请求队列不为空时,那么这个线程将处于与其他线程竞争资源的状态,谁获得锁谁就获得了处理事件的资格。
6. 如果同时1000个客户端进行访问请求,线程数不多,怎么能及时响应处理每一个呢?
本项目是通过对子线程循环调用来解决高并发的问题的。
首先在创建线程的同时就调用了pthread_detach将线程进行分离,不用单独对工作线程进行回收,资源自动回收。
我们通过子线程的run调用函数进行while循环,让每一个线程池中的线程永远都不会停止,访问请求被封装到请求队列(list)中,如果没有任务线程就一直阻塞等待,有任务线程就抢占式进行处理,直到请求队列为空,表示任务全部处理完成。
除此之外,该项目采用了I/O多路复用技术,当客户连接有事件需要处理时,epoll会进行事件提醒,然后将对应的任务加入请求队列,等待工作线程的竞争,不仅如此 ,epoll的et(水平触发模式)可以及时的处理每一个线程的请求。
7. 如果一个客户请求需要占用线程很久的时间,会不会影响接下来的客户请求呢,有什么好的策略呢?
会,因为线程池内线程的数量时有限的,如果客户请求占用线程时间过久的话会影响到处理请求的效率,当请求处理过慢时会造成后续接受的请求只能在请求队列中等待被处理,从而影响接下来的客户请求。
应对策略(定时器):
我们可以为线程处理请求对象设置处理超时时间, 超过时间先发送信号告知线程处理超时,然后设定一个时间间隔再次检测,若此时这个请求还占用线程则直接将其断开连接。
8. 什么是虚假唤醒?
虚假唤醒指的是某个线程被错误地唤醒,而实际上没有任何满足它被唤醒的条件。这种现象常常发生在使用条件变量来实现线程同步时。
举个例子,我们现在有一个生产者-消费者队列和三个线程。
1) 1号线程从队列中获取了一个元素,此时队列变为空。
2) 2号线程也想从队列中获取一个元素,但此时队列为空,2号线程便只能进入阻塞(cond.wait()),等待队列非空。
3) 这时,3号线程将一个元素入队,并调用cond.notify()唤醒条件变量。
4) 处于等待状态的2号线程接收到3号线程的唤醒信号,便准备解除阻塞状态,执行接下来的任务(获取队列中的元素)。
5) 然而可能出现这样的情况:当2号线程准备获得队列的锁,去获取队列中的元素时,此时1号线程刚好执行完之前的元素操作,返回再去请求队列中的元素,1号线程便获得队列的锁,检查到队列非空,就获取到了3号线程刚刚入队的元素,然后释放队列锁。
6) 等到2号线程获得队列锁,判断发现队列仍为空,1号线程“偷走了”这个元素,所以对于2号线程而言,这次唤醒就是“虚假”的,它需要再次等待队列非空。
9. 如何避免虚假唤醒?
为了防止这种情况,通常的做法是在等待条件变量时,使用一个条件循环来重新检查唤醒条件。这种结构通常如下:
std::queue<int> queue;
std::mutex mtx;
std::condition_variable cv;void consumer() {std::unique_lock<std::mutex> lock(mtx);while (queue.empty()) {cv.wait(lock); // 等待被唤醒}// 唤醒后再检查队列是否为空,避免虚假唤醒造成的误操作int task = queue.front();queue.pop();// 处理任务
}void producer() {std::unique_lock<std::mutex> lock(mtx);queue.push(1); // 向队列中添加任务cv.notify_one(); // 通知消费者线程
}
解释:
- 当消费者被唤醒时,不是立即执行任务,而是先通过
while(queue.empty())重新检查条件是否满足。如果条件不满足,消费者将继续等待,直到条件真正满足(即队列中有任务)。 - 这种额外的条件检查可以避免虚假唤醒带来的问题,确保线程在正确的条件下执行。
总结来说,在多线程环境中,虚假唤醒是正常现象,但通过在唤醒后重新检查条件的方式,程序可以有效防止虚假唤醒导致的逻辑错误。
10. 如何销毁线程?
1、通过判断标志位,主动退出(比如出现超时和报错)
2、通过Thread类中成员方法interrupt(),主动退出(发送中断信号)
3、通过Thread类中成员方法stop(),强行退出

11. detach和join有什么区别?
(1)当调用join(),主线程等待子线程执行完之后,主线程才可以继续执行,此时主线程会释放掉执行完后的子线程资源。主线程等待子线程执行完,可能会造成性能损失。
(2)当调用detach(),主线程与子线程分离,他们成为了两个独立的线程遵循cpu的时间片调度分配策略。子线程执行完成后会自己释放掉资源。分离后的线程,主线程将对它没有控制权。
当你确定程序没有使用共享变量或引用之类的话,可以使用detch函数,分离线程。
12. 线程池中有多少个线程,线程池数量如何设定
默认8个
调整线程池中的线程数量的最主要的目的是为了充分并合理地使用 CPU 和内存等资源,从而最大限度地提高程序的性能。
Ncpu 表示 CPU的数量。
如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 Ncpu+1能够实现最优的CPU 利用率,+1 是保证当线程由于页缺失故障(操作系统)或其它原因 导致暂停时,额外的这个线程就能顶上去,保证CPU 时钟周期不被浪费
如果是IO密集型任务,参考值可以设置为 2 * Ncpu。因为线程间竞争的不是CPU的计算资源而是IO,IO的处理一般较慢,多于cores数的线程将为CPU争取更多的任务,不至在线程处理IO的过程造成CPU空闲导致资源浪费
最佳线程数量 = ((线程等待时间+线程CPU时间)/ 线程CPU时间)* CPU个数。
由公式可得,线程等待时间所占比例越高,需要越多的线程,线程CPU时间所占比例越高,所需的线程数越少。
12. 线程越多越好么
随着线程数越多,效率越来越高,但到一个峰值,再增加线程数量时,就会出现问题。线程太多要来回的切换,最终可能线程切换所用时间比执行时间业务所用时间还大。
并发模型相关
1.简单说一下服务器使用的并发模型?
2.reactor、proactor、主从reactor模型的区别?
3.你用了epoll,说一下为什么用epoll,还有其他复用方式吗?区别是什么?
4.介绍一下几种I/O模型
下面是几种常见的 I/O 模型:
1. 阻塞 I/O(Blocking I/O)
在阻塞 I/O 模型中,应用程序执行 I/O 操作时会被阻塞,直到操作完成。例如,当应用程序请求从磁盘读取数据时,如果数据不可用,程序将会在调用处停止执行,直到数据被读取完成。这种模型简单但效率较低,因为在等待 I/O 操作完成期间,程序不能执行其他任务。
2. 非阻塞 I/O(Non-blocking I/O)
在非阻塞 I/O 模型中,应用程序执行 I/O 操作时不会被阻塞。如果 I/O 操作不能立即完成(如数据未就绪),操作会立即返回一个错误(通常是 EWOULDBLOCK 或 EAGAIN),应用程序可以继续执行其他任务。应用程序需要不断地查询 I/O 操作是否完成,这种方式称为轮询。
3. I/O 多路复用(I/O Multiplexing)
I/O 多路复用允许应用程序同时监控多个 I/O 流的事件。常用的实现有 select、poll 和 epoll(仅在 Linux 上)。应用程序使用这些调用等待多个 I/O 流中的任何一个变为就绪状态。当 I/O 流就绪,即数据可读或可写时,应用程序会被唤醒以处理该事件。这种模型适合处理大量并发连接,因为单个线程可以管理多个网络连接。
4. 信号驱动 I/O(Signal-driven I/O)
在信号驱动 I/O 模型中,应用程序首先对一个 I/O 流进行信号处理配置,然后继续执行其他任务;当 I/O 流就绪时,操作系统会发送一个信号通知应用程序。然后,应用程序可以启动 I/O 操作来处理数据。这种模型允许应用程序异步地处理 I/O 事件,但并不广泛使用。
5. 异步 I/O(Asynchronous I/O)
异步 I/O 模型是最高效的一种,应用程序发起 I/O 操作后可以立即继续执行其他任务。与信号驱动 I/O 不同,异步 I/O 的操作系统不仅通知应用程序 I/O 流就绪,而且会自动完成数据的传输。应用程序在 I/O 操作完成后会收到一个通知。这种模型最大化了程序的运行效率,因为应用程序无需在任何点等待 I/O 操作完成。
总结
这些 I/O 模型各有特点,适用于不同的应用场景。阻塞和非阻塞 I/O 更简单、易于理解,而 I/O 多路复用和异步 I/O 提供了更高的性能和灵活性,特别适合于网络服务和高并发应用。选择合适的 I/O 模型是提升应用性能的关键步骤。
5.简单说一下服务器使用的并发模型?两种高效的事件并发处理模式reactor、proactor?主从reactor模型
事件:I/O事件、信号及定时事件
(1)reactor模式中,主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话立即通知工作线程(逻辑单元 ),将socket可读写事件放入请求队列,交给工作线程处理,即读写数据、接受新连接及处理客户请求均在工作线程中完成。通常由同步I/O实现(epoll_wait)。
(2)proactor模式中,主线程和内核负责处理读写数据、接受新连接等I/O操作,工作线程仅负责业务逻辑,如处理客户请求。通常由异步I/O实现(aio_read/aio_write)。
由于异步I/O并不成熟,实际中使用较少,本服务器采用:同步I/O模拟Proactor模式
同步I/O模型的工作流程如下(epoll_wait为例):
主线程往epoll内核事件表注册socket上的读就绪事件。
主线程调用epoll_wait等待socket上有数据可读
当socket上有数据可读,epoll_wait通知主线程,主线程从socket循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。
睡眠在请求队列上某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件
主线程调用epoll_wait等待socket可写。
当socket上有数据可写,epoll_wait通知主线程。主线程往socket上写入服务器处理客户请求的结果。
(3) 主从Reactor模式:核心思想是,主反应堆线程只负责分发Acceptor连接建立,已连接套接字上的I/O事件交给sub-reactor负责分发。其中 sub-reactor的数量,可以根据CPU的核数来灵活设置。
主反应堆线程一直在感知连接建立的事件,如果有连接成功建立,主反应堆线程通过accept方法获取已连接套接字,接下来会按照一定的算法选取一个从反应堆线程,并把已连接套接字加入到选择好的从反应堆线程中。主反应堆线程唯一的工作,就是调用accept获取已连接套接字,以及将已连接套接字加入到从反应堆线程中。
相关文章:
TinyWebSever项目面试题整理
TinyWebSever项目面试题整理 1.为什么要做这样一个项目? 满足高并发和高性能需求:现代Web应用面对大量用户,Web服务器需要高效处理并发连接。比如通过线程池、非阻塞I/O、事件驱动机制(如epoll),Web服务器…...
维修保养记录接口-维修保养记录API-汽车接口
维修保养记录接口的使用主要涉及到API对接和在线查询两种方式。以下是详细的使用步骤和注意事项: 一、API对接 注册与申请: 首先,你需要在提供维修保养记录接口的平台(如挖数据平台、第三方数据服务商等)进行注册&…...
基于 RealSense D435相机实现手部姿态检测
基于 RealSense D435i相机进行手部姿态检测,其中采用 Mediapipe 进行手部检测,以下是详细步骤: Mediapipe 是一个由 Google开发的开源框架,专门用于构建多媒体处理管道,特别是计算机视觉和机器学习任务。它提供了一系列…...
linux 下mailx 的使用。发送短信
1. 安装 mailx yum install -y mailx 2.请求数字证书 163 邮箱 mkdir -p /root/.certs/ ####创建目录,用来存放证书 echo -n | openssl s_client -connect smtp.163.com:465 | sed -ne /-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p >…...
把网易云音乐的网页源码复制出来,粘贴在hbuilder中,运行于浏览器,为什么没有任何内容显示?
在将网易云音乐的网页源码复制并粘贴到HBuilder中后,如果运行于浏览器时没有任何内容显示,这可能是由于以下几个原因造成的: 1. 外部资源加载问题 资源路径错误:网易云音乐的网页源码中可能包含大量的外部资源链接,如CSS、JavaScript文件、图片等。当这些资源链接的路…...
excel怎么转换json
如何将 Excel 转换为 JSON 方法一:使用内置函数 在 Excel 中选择要转换的数据范围。 转到“数据”选项卡 > “获取外部数据”组 > “自其他来源” > “JSON”。 在“从文件”对话框中,选择要保存 JSON 文件的位置,然后单击“导入”…...
二、认识大模型
认识大模型 什么是大模型?发展趋势AGI是不是泡沫大模型对比【时效】大模型特点大模型技术原理向量化除了向量化,大模型还具有特征提取特点 总结结语 什么是大模型? 大模型是大规模语言模型(Large Language Model)的简…...
2024年【电工(高级)】考试题及电工(高级)考试内容
题库来源:安全生产模拟考试一点通公众号小程序 电工(高级)考试题根据新电工(高级)考试大纲要求,安全生产模拟考试一点通将电工(高级)模拟考试试题进行汇编,组成一套电工…...
Unity中分辨率适配
在Unity中,分辨率适配问题是一个常见的挑战,尤其是在开发跨平台游戏时。为了确保你的游戏在不同设备上都能良好显示,以下是一些解决方案和最佳实践: 1. 使用Canvas Scaler 在UI的Canvas组件中,设置 UI Scale Mode …...
图像处理基础知识点简记
简单记录一下图像处理的基础知识点 一、取样 1、释义 图像的取样就是图像在空间上的离散化处理,即使空间上连续变化的图像离散化, 决定了图像的空间分辨率。 2、过程 简单描述一下图象取样的基本过程,首先用一个网格把待处理的图像覆盖,然后把每一小格上模拟图像的各个…...
微信小程序-使用vant组件库
文章目录 微信小程序-使用vant组件库概述构建npm构建步骤使用vant注册使用添加事件使用插槽 样式覆盖解除样式隔离使用外部样式类使用CSS变量 微信小程序-使用vant组件库 概述 Vant Weapp 是有赞前端团队开源的小程序 UI 组件库,基于微信小程序的自定义组件开发&a…...
Java【注解】
概述 ①Java的注解又称标注,它是程序的元数据,也是程序代码的标记,主要添加到程序代码上,作说明和解释。元数据是用来描述数据的一种数据。 ②Java中的注解可用于类、构造方法、成员变量、方法、参数等的声明中,注解…...
基于安卓开发大型体育场管理系统的设计与实现(源码+定制+讲解)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
【Go】-Websocket的使用
目录 为什么需要websocket 使用场景 在线教育 视频弹幕 Web端即时通信方式 什么是web端即时通讯技术? 轮询 长轮询 长连接 SSE websocket 通信方式总结 Websocket介绍 协议升级 连接确认 数据帧 socket和websocket 常见状态码 gorilla/websocket实…...
怎么查看员工电脑安装了什么软件
1、使用专业监控软件:安装如金刚钻信息网站行为审计系统、WorkWin等专业的电脑监控软件。这些软件能够实时监控员工的电脑操作,包括安装的软件、运行的程序等。通过软件的管理端,您可以轻松查看员工电脑上安装的所有软件,并可以设…...
面积开运算bwareaopen
一个非常有用的二值图像形态学后处理算法,建立在连通分量分析的基础之上。 bwareaopen 从二值图像中删除小对象 语法 BW2 bwareaopen(BW,P) BW2 bwareaopen(BW,P,conn) 说明 BW2 bwareaopen(BW,P) 从二值图像 BW 中删除少于 P 个像素的所有连通分量&#x…...
TortoiseGit 下载和安装
下载 1,下载路径 Download – TortoiseGit – Windows Shell Interface to Git 2,选择windows64的, 3,下载完成后 安装 1,双击运行,点击next 2,点击next 3,点击next 4࿰…...
0x09 瑞友 应用虚拟化系统 GetBSAppUrl SQL注入漏洞 - 复现
参考:瑞友 应用虚拟化系统 GetBSAppUrl SQL注入漏洞 | PeiQi文库 (wgpsec.org) 漏洞描述 瑞友应用虚拟化系统中的 GetBSAppUrl 方法存在 SQL注入漏洞。由于请求参数未经过滤,攻击者可以利用此漏洞执行恶意SQL查询,从而获取数据库中的敏感信息。 漏洞影响 受影响版本:瑞友…...
C++(Qt)软件调试---内存调试器Dr.Memory(21)
C(Qt)软件调试—内存调试器Dr. Memory(21) 文章目录 C(Qt)软件调试---内存调试器Dr. Memory(21)[toc]1、概述🐜2、安装Dr.Memory🪲3、命令行使用Dr.Memory🦗4、Qt Creator集成使用Dr.Memory&…...
Python3自带HTTP服务:轻松开启与后台管理
Python3自带有http服务,可以在服务器,也可以在本地启动,并运行一些常用的网页程序。比如:我们可以把streamlit框架编写的网页放到服务器上,开启http服务,就可以通过网页来调用这个pythont程序了,…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
