【Linux】寿司线程池{单例模式之懒汉模式下的线程池}
文章目录
- 回顾单例模式
- 0.多线程下的单例模式的意义
- 1.什么是单例模式
- 1.0设计模式
- 1.1C++单例模式的介绍及原理
- 1.2拷贝构造和赋值重载的处理
- 1.3if (nullptr == ptr),nullptr放在比较运算符的前面?
- 1.4实现单例模式的方式
- 2.实现懒汉方式的单例模式
- 2.1单线程的单例模式
- 2.2多线程的单例模式
- 3.总结线程池
- 4.代码
- singleThreadPool.hpp
- singleTestMain.cc
回顾单例模式
在cpp专栏,我们已经讲过单例模式。特殊类设计[下] — 单例模式
0.多线程下的单例模式的意义
在多线程环境下,单例模式的目的是确保无论有多少线程尝试访问或创建类的实例,都只有一个实例被创建,并且这个实例可以被所有线程共享和访问。这是因为在多线程编程中,如果没有适当的同步机制,多个线程可能同时尝试创建类的实例,从而导致产生多个实例,这与单例模式的初衷相违背。
单例模式在多线程环境下的实现需要特别注意线程安全性。如果没有正确的同步,多个线程可能会同时检查单例是否已经被创建,并且都得出否定的结论,从而都尝试创建新的实例。这会导致产生多个实例,破坏单例模式的约束。
因此,多线程下的单例模式的主要目的是:
确保唯一性:不论何时何地,整个应用程序范围内都只有一个类的实例存在。
线程安全:确保在多线程环境下,类的实例的创建和访问都是安全的,不会出现数据竞争或不一致的情况。
全局访问点:提供一个全局的访问点来获取这个唯一的实例,使得任何需要的地方都可以方便地获取和使用这个实例。
为了实现线程安全的单例模式,通常需要使用互斥锁(如std::mutex)或其他同步机制来确保在创建实例时的线程安全。这样,即使多个线程同时尝试访问或创建单例,也只有一个线程能够成功创建实例,而其他线程将获取已经创建好的实例。
1.什么是单例模式
1.0设计模式
IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式
1.1C++单例模式的介绍及原理
单例模式是一种 “经典的, 常用的, 常考的” 设计模式.
单例模式是一种设计模式,它保证一个类仅有一个实例,并提供一个全局访问点来访问该实例。这种模式在许多场景下都很有用,例如,你可能需要一个唯一的配置管理器、资源管理器或线程池。
在C++中,实现单例模式的基本原理如下:
将类的构造函数设为私有,以防止外部代码通过new来创建类的实例。
在类内部提供一个静态的私有成员变量来保存类的唯一实例。
提供一个公开的静态成员函数(通常命名为getInstance或Instance),用于返回类的唯一实例。如果实例尚未创建,该函数将负责创建它。
实现一个简易的单例模式至少要具备的条件
要实现一个简易的单例模式,你需要满足以下条件:
私有构造函数:确保类的实例不能从类外部被创建。
静态私有成员变量:用于存储类的唯一实例。
公开的静态成员函数:用于获取类的唯一实例。这个函数应该在第一次被调用时创建实例,并在后续的调用中返回已创建的实例。
以下是一个简单的C++用懒汉模式的单例模式实现示例:
cpp
class Singleton {
private: // 私有构造函数,防止外部创建实例 Singleton() {} // 静态私有成员变量,保存唯一实例 static Singleton* instance; public: // 公开的静态成员函数,用于获取唯一实例 static Singleton* getInstance() { if (!instance) { instance = new Singleton(); } return instance; } // 假设的某个成员函数 void someFunction() { // ... } // 析构函数设为公有,但通常不需要外部直接调用 ~Singleton() { // 清理资源 }
};
// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;
在这个示例中,Singleton类只有一个私有构造函数,因此不能从类外部创建其实例。getInstance函数是获取Singleton类唯一实例的入口点。如果instance为nullptr(即尚未创建实例),则getInstance会创建一个新的实例。在后续调用中,getInstance将直接返回已创建的实例。
注意:上述实现不是线程安全的。在多线程环境下,多个线程可能同时检查instance是否为nullptr并尝试创建实例,从而导致创建多个实例。为了解决这个问题,可以使用互斥锁(如std::mutex)或其他同步机制来确保在任意时刻只有一个线程可以执行创建实例的代码。此外,为了避免内存泄漏,通常还需要提供一个全局的清理函数来删除单例实例。
1.2拷贝构造和赋值重载的处理
在C++单例模式的实现中,拷贝构造函数和赋值操作符通常是不需要的,因此将它们设为私有并禁止外部使用是一个常见的做法。这是因为单例模式要求一个类只有一个实例,而拷贝构造和赋值操作会允许创建类的多个实例,这与单例模式的原则相违背。
对于拷贝构造函数,你可以这样处理:
cpp
class Singleton {
private: // 禁止拷贝构造 Singleton(const Singleton&) = delete; // ... 其他成员 ...
};
对于赋值操作符,你也可以禁止它:
cpp
class Singleton {
private: // 禁止赋值操作 Singleton& operator=(const Singleton&) = delete; // ... 其他成员 ...
};
= delete是C++11引入的特性,用于显式地禁止某个成员函数的使用。编译器将尝试调用这些函数时会产生编译错误。
通过禁止拷贝构造和赋值操作,你确保了单例模式的正确性,因为即使尝试进行拷贝或赋值,编译器也会阻止这样的操作。
此外,对于单例模式来说,还需要考虑线程安全的问题。在多线程环境下,需要确保getInstance函数的线程安全性,以避免多个线程同时创建实例。这通常可以通过使用互斥锁(如std::mutex)或其他同步机制来实现。
最后,为了防止内存泄漏,你可能还需要在程序结束时删除单例实例。这可以通过在单例类中添加一个静态的清理函数来实现,该函数在程序结束时调用单例实例的析构函数。但是,请注意,在现代C++中,通常依赖智能指针(如std::shared_ptr)和程序终止时的全局对象析构顺序来自动管理单例实例的生命周期,而不需要显式地编写清理代码。
1.3if (nullptr == ptr),nullptr放在比较运算符的前面?
在C++中,将nullptr放在比较运算符==或!=的前面是一种良好的编程习惯,尤其是在与指针进行比较时。这样做的好处主要体现在两个方面:
防止误写为赋值运算符:
将nullptr放在前面可以防止你不小心将比较运算符==误写为赋值运算符=。如果nullptr在后面,例如ptr = nullptr,当你意图进行比较而误写为赋值时,编译器不会产生错误,但代码的逻辑会完全改变。这可能导致难以察觉的逻辑错误。通过将nullptr放在前面,你可以更容易地识别和纠正这种错误,因为nullptr = ptr这样的写法在语法上是错误的,编译器会立即报错。
代码一致性和可读性:
将常量放在比较运算符的左边可以增强代码的一致性。在C++中,常量通常应该放在比较运算符的左侧,这样可以使代码看起来更加统一和整洁。此外,这种写法也符合一些编程规范和风格指南,有助于提高代码的可读性。
举个例子,假设你有一个指针ptr,你应该这样写比较语句:
cpp
if (nullptr == ptr) { // ptr 是 nullptr
}
而不是这样:cpp
if (ptr == nullptr) { // ptr 是 nullptr
}
虽然第二种写法在逻辑上是正确的,但第一种写法提供了额外的安全性,防止了可能的误写。
然而,值得注意的是,现代编译器和IDE通常都有强大的代码检查和重构工具,可以帮助你避免这类错误。因此,即使你将nullptr放在比较运算符的右侧,只要你的编程环境配置得当,也可以有效地避免误写为赋值运算符的问题。不过,遵循将常量放在比较运算符左侧的约定仍然是一个好习惯,因为它可以提高代码的一致性和可读性。
1.4实现单例模式的方式
饿汉实现方式和懒汉实现方式
吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭。不用的时候就准备好了,要用的时候直接拿来用。
吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式.懒汉方式最核心的思想是 “延时加载”. 从而能够优化服务器的启动速度。用的时候再准备。
2.实现懒汉方式的单例模式
2.1单线程的单例模式
static singleThreadPool<T> *getThreadPoolInstance(int threadNum = g_threadNum)
{if (nullptr == ptrThreadPool){ptrThreadPool = new singleThreadPool<T>(threadNum);}return ptrThreadPool;
}
2.2多线程的单例模式
static singleThreadPool<T> *getThreadPoolInstance(int threadNum = g_threadNum)
{if (nullptr == ptrThreadPool){ptrThreadPool = new singleThreadPool<T>(threadNum);}return ptrThreadPool;
}
- 单线程下的单例模式代码拿到多线程场景中,需要考虑的【线程安全问题】只发生在第一次创建单例时,即第一次可能有多个线程在执行if语句,这样就可能有不止一个线程获取到了单例,基于这种情况,我们进行了第一步改版。如下。
static singleThreadPool<T> *getThreadPoolInstance(int threadNum = g_threadNum)
{{lockGuard lockguard(&mutex);if (nullptr == ptrThreadPool){ptrThreadPool = new singleThreadPool<T>(threadNum);}}return ptrThreadPool;
}
- 这样并不完全对。我们还要考虑,第一次的某个线程成功获取了单例,即这个线程拿着指向一个单例对象的ptrThreadPool走了,之后的线程再次调用getThreadPoolInstance接口想获取到这个唯一的单例时,需要申请锁–执行if–if不满足–解锁–返回ptrThreadPool。问题来了,之后的线程虽然没有创建单例但是获取到了这个唯一的单例,即之后的线程可以获取到唯一的单例,多线程下的单例模式似乎已经搞定了。但是,这样会存在大量的申请和释放锁的行为,这毫无意义且浪费资源,即之后的线程在获取单例时,if一定是不满足的,这时压根没必要进行加锁和解锁,但是为了保证第一次创建单例是安全的,加锁的行为必须有,之后的线程申请到了锁那还好,如果申请不到还要等待⇒ 这毫无意义且浪费资源。做进一步优化如下。
static singleThreadPool<T> *getThreadPoolInstance(int threadNum = g_threadNum){if (nullptr == ptrThreadPool){lockGuard lockguard(&mutex);if (nullptr == ptrThreadPool){ptrThreadPool = new singleThreadPool<T>(threadNum);}}return ptrThreadPool;}
这样写的优势
假定【后续线程】:第一次已经有某个线程创建了单例即单例已存在。那么这样写的优势:有效减少后续线程进行加锁检测的问题,拦截大量后续线程直接访问锁的行为
3.总结线程池
- 设计一个线程池,这个线程池有多个线程在运行,一旦接收到任务,则某个线程就会去获取并执行任务。
- 可能有多个线程充当派发任务的一方,这时就引入了【单例模式】版的线程池,旨在让不同的线程共享唯一的一个线程池实例,任务方线程们都向这个单例线程池派发任务。
- 上面的代码我们只创建了一个任务派送方【main线程】,但是单例模式我们已经写好了,读者们可以创建多个任务派送方测试单例模式是否正确。
- 由于我们设计的主线程是死循环,我们并没有维护单例的回收,单例的回收我们在特殊类设计[下] — 单例模式已经讲过。
4.代码
其余代码与【Linux】认识线程池 AND 手撕线程池(正常版)相同,点击获取即可。
singleThreadPool.hpp
#pragma once#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>
#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"const int g_threadNum = 3;template <class T>
class singleThreadPool
{
public:pthread_mutex_t *getMutex(){return &lock;}void waitCond(){pthread_cond_wait(&cond, &lock);}bool isEmpty(){return _taskQueue.empty();}T getTask(){T t = _taskQueue.front();_taskQueue.pop();return t;}static void *startRoutine(void *args){ThreadInfo *td = (ThreadInfo *)args;singleThreadPool<T> *tp = (singleThreadPool<T> *)td->_ptrThreadPool;while (true){T task;{lockGuard lockguard(tp->getMutex());while (tp->isEmpty())tp->waitCond();task = tp->getTask();}task(td->_threadName);}}private:// 构造函数singleThreadPool(int thread_num = g_threadNum): _threadNum(thread_num){pthread_mutex_init(&lock, nullptr);pthread_cond_init(&cond, nullptr);for (int i = 1; i <= _threadNum; i++){// 初始化列表区域 对象还未存在 走到函数块{}内 对象已存在 可以使用this指针_threads.push_back(new Thread(i, startRoutine, this));}}singleThreadPool(const singleThreadPool<T> ©) = delete;const singleThreadPool<T> &operator=(const singleThreadPool<T> ©) = delete;public:// 考虑 多线程使用单例 的情况static singleThreadPool<T> *getThreadPoolInstance(int threadNum = g_threadNum){if (nullptr == ptrThreadPool){lockGuard lockguard(&mutex);if (nullptr == ptrThreadPool){ptrThreadPool = new singleThreadPool<T>(threadNum);}}return ptrThreadPool;}void run(){for (auto &iter : _threads){iter->start();// std::cout << iter->name() << " 启动成功" << std::endl;logMsg(NORMAL, "%s %s", iter->name().c_str(), "启动成功");}}void pushTask(const T &task){lockGuard lockguard(&lock);_taskQueue.push(task);pthread_cond_signal(&cond);}~singleThreadPool(){for (auto &iter : _threads){iter->join();delete iter;}pthread_mutex_destroy(&lock);pthread_cond_destroy(&cond);}private:int _threadNum;std::vector<Thread *> _threads;std::queue<T> _taskQueue;pthread_mutex_t lock;pthread_cond_t cond;volatile static singleThreadPool<T> *ptrThreadPool;懒汉是调用的时候才会赋值,如果创建了对象这个指针实际已经指向了单例,如果指针一开始被优化到寄存器里了,那第二次后续线程访问这个指针就是null,后续不从内存里读取的话就会一直是null了static pthread_mutex_t mutex;
};// 类内声明 类外初始化
template <typename T>
singleThreadPool<T> *singleThreadPool<T>::ptrThreadPool = nullptr;template <typename T>
pthread_mutex_t singleThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;
singleTestMain.cc
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <unistd.h>#include "singleThreadPool.hpp"
#include "Task.hpp"int main()
{srand((unsigned long)time(nullptr) ^ getpid());// ThreadPool<Task> *tp = new ThreadPool<Task>();// ThreadPool<Task> *tp = ThreadPool<Task>::getThreadPoolInstance();//tp->run();singleThreadPool<Task>::getThreadPoolInstance()->run();while (true){// 生产数据/制作任务 -- 耗费时间int x = rand() % 10 + 1;usleep(1000);int y = rand() % 5 + 1;Task t(x, y, [](int x, int y) -> int{ return x + y; });logMsg(DEBUG, "Main-Pro 发送任务: %d+%d=未知", x, y);// 推送任务到线程池中// tp->pushTask(t);singleThreadPool<Task>::getThreadPoolInstance()->pushTask(t);sleep(1);}return 0;
}
相关文章:
【Linux】寿司线程池{单例模式之懒汉模式下的线程池}
文章目录 回顾单例模式0.多线程下的单例模式的意义1.什么是单例模式1.0设计模式1.1C单例模式的介绍及原理1.2拷贝构造和赋值重载的处理1.3if (nullptr ptr),nullptr放在比较运算符的前面?1.4实现单例模式的方式 2.实现懒汉方式的单例模式2.1单线程的单例模式2.2多…...
Docker资源管理和分配指南
什么是cgroup? cgroups其名称源自控制组群(control groups)的简写,是Linux内核的一个功能,用来限制、控制与分离一个进程组(如CPU、内存、磁盘输入输出等)。 什么是Docker资源限制?…...
为什么索引的底层结构是B+树
B树 1.数据库与数据交互的单位是page,而B树的每个节点都是一个page,访问一个节点,就相当于进行了一次I/O操作。所以访问的节点越少,查找效率越大。而B树是矮胖的,查找深度也不会太大。 2.B树中的节点是有序存储的,对于范围查询、排…...
NLP学习路线指南总结
当然可以,以下是一份较为详细的NLP学习路线指南,帮助你逐步掌握自然语言处理的核心技术和应用。 一、基础知识与技能 语言学基础: 语言学基本概念:语音、语法、语义等。语言的层次与分类:语音学、音系学、句法学、语…...
试过了,ChatGPT确实不用注册就可以使用了!
看到官网说不用登录也可以直接使用ChatGPT 我们来试一下 直接打开官网 默认是直接进入了chatgpt3.5的聊天界面 之前是默认进的登录页面 聊一下试试 直接回复了,目前属于未登录状态,挺好! 来试下ChatGPT4 跳转到了登录页面 目前来看gpt4还…...
CANoe自带的TCP/IP协议栈中TCP的keep alive机制是如何工作的
TCP keep alive机制我们已经讲过太多次,车内很多控制器的TCP keep alive机制相信很多开发和测试的人也配置或者测试过。我们今天想知道CANoe软件自带的TCP/IP协议栈中TCP keep alive机制是如何工作的。 首先大家需要知道TCP keep alive的参数有哪些?其实就三个参数:CP_KEEP…...
【C++练级之路】【Lv.18】哈希表(哈希映射,光速查找的魔法)
快乐的流畅:个人主页 个人专栏:《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火,在为久候之人燃烧! 文章目录 引言一、哈希1.1 哈希概念1.2 哈希函数1.3 哈希冲突 二、闭散列2.1 数据类型2.2 成员变量2.3 默认成员函数2.…...
「PHP系列」If...Else语句/switch语句
文章目录 一、If...Else语句1. 基本语法2. 带有 elseif 的语法3. 示例示例 1:基本 if...else 结构示例 2:使用 elseif示例 3:嵌套 if...else 结构 4. 注意事项 二、switch语句1. 基本语法2. 示例示例 1:基本 switch 结构示例 2&am…...
Ubuntu部署BOA服务器
BOA服务器概述 BOA是一款非常小巧的Web服务器,源代码开放、性能优秀、支持CGI通用网关接口技术,特别适合用在嵌入式系统中。 BOA服务器主要功能是在互联嵌入式设备之间进行信息交互,达到通用网络对嵌入式设备进行监控,并将反馈信…...
安卓Glide加载失败时点击按钮重新加载图片
需求 假设此时已经用load指定一个url: String,又用into指定了一个img: ImageView开始加载,但是网络突然中断,导致图片加载失败。在这种情况下,想要通过点击一个Button重新加载。 Glide.with(context).load(url).placeholder(loa…...
linux下python服务定时(自)启动
AI应用开发相关目录 本专栏包括AI应用开发相关内容分享,包括不限于AI算法部署实施细节、AI应用后端分析服务相关概念及开发技巧、AI应用后端应用服务相关概念及开发技巧、AI应用前端实现路径及开发技巧 适用于具备一定算法及Python使用基础的人群 AI应用开发流程概…...
awk命令进阶操作(二)
awk模块 awk模块awk的BEGIN模块和END模块BEGIN模块BEGIN 常见错误END模块END模块 常见错误 案例计算1~100的累加和统计系统中有多少用户的shell类型是/bin/bash awk模块 awk的BEGIN模块和END模块 格式 awk BEGIN{}{}END{} 文件名BEGIN模块 用于定义一个动作,用{…...
【洛谷 P8695】[蓝桥杯 2019 国 AC] 轨道炮 题解(映射+模拟+暴力枚举+桶排序)
[蓝桥杯 2019 国 AC] 轨道炮 题目描述 小明在玩一款战争游戏。地图上一共有 N N N 个敌方单位,可以看作 2D 平面上的点。其中第 i i i 个单位在 0 0 0 时刻的位置是 ( X i , Y i ) (X_i, Y_i) (Xi,Yi),方向是 D i D_i Di (上下左右之一, 用…...
高阶DS---AVL树详解(每步配图)
目录 前言: AVL树的概念: AVL树节点的定义: AVL树的插入(重点) AVL树的旋转: (1)新节点插入较高左子树的左侧---右单旋 (2)新节点插入较高右子树的右侧---左单旋 …...
c++前言
目录 1. 什么是 C 2. C 发展史 3. C 的重要性 4. 如何学习 C 5. 关于本门课程 1. 什么是C C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的 程序,需要高度的抽象和建模时, C 语言则不合适…...
2024年泰迪杯数据挖掘B题详细思路代码文章教程
目前b题已全部更新包含详细的代码模型和文章,本文也给出了结果展示和使用模型说明。 同时文章最下方包含详细的视频教学获取方式,手把手保姆级,模型高精度,结果有保障! 分析: 本题待解决问题 目标&#…...
练习 21 Web [GXYCTF2019]BabySQli
SQL联合查询,注意有源码看源码,Base64以及32的区别,MD5碰撞 打开后有登录框,先随意登录尝试 只有输入admin才是返回wrong pass! 其他返回wrong user 所以用户名字段一定要输入admin 养成好习惯,先查看源码…...
【并发编程】CountDownLatch
📝个人主页:五敷有你 🔥系列专栏:并发编程 ⛺️稳中求进,晒太阳 CountDownLatch 概念 CountDownLatch可以使一个获多个线程等待其他线程各自执行完毕后再执行。 CountDownLatch 定义了一个计数器,…...
2024-HW --->SSRF
这不是马上准备就要护网了嘛,如火如荼的报名ing!!!那么小编就来查缺补漏一下以前的web漏洞,也顺便去收录一波poc!!!! 今天讲的主人公呢就是SSRF,以前学的时候…...
该主机与 Cloudera Manager Server 失去联系的时间过长。 该主机未与 Host Monitor 建立联系
该主机与 Cloudera Manager Server 失去联系的时间过长。 该主机未与 Host Monitor 建立联系 这个去集群主机cm界面上看会出现这个错误 排查思路: 一般比较常见的原因可能是出问题的主机和集群主节点的时间对应不上了。还有就是cm agent服务出现问题了 去该主机的…...
【BUG】No module named ‘dnf‘
报错内容: 类型一 # git clone https://github.com/pytorch/vision.git Cloning into vision... /usr/libexec/git-core/git-remote-https: symbol lookup error: /usr/lib64/libldap.so.2: undefined symbol: EVP_md2, version OPENSSL_1_1_0类型二 # yum reins…...
Ubuntu pycharm配置Conda环境
参考博客:https://blog.csdn.net/qq_40726937/article/details/105323965 https://juejin.cn/post/7229543139950051388 Ubuntu20.04中搭建虚拟环境并且用pycharm调用Ubuntu中的虚拟环境。_ubuntu pycharm的虚拟环境选哪个-CSDN博客...
工作体验记录
文章目录 如何提高说话能力?如何提高行动力?如何完成一个任务产出成果?如何寻找突破点提高解决问题的效率?如何成为技术领导?参考资料 如何提高说话能力? 三思而后说,想清楚问题描述,抓住重点…...
YOLO火灾烟雾检测数据集:20000多张,yolo标注完整
YOLO火灾烟雾检测数据集:一共20859张图像,yolo标注完整,部分图像应用增强 适用于CV项目,毕设,科研,实验等 需要此数据集或其他任何数据集请私信...
基于Spring Boot的餐厅点餐系统
基于Spring Boot的餐厅点餐系统 开发语言:Java框架:springbootJDK版本:JDK1.8数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9 部分系统展示 管理员登录界面 用户注册登录界面 …...
tkinter控件教程使用说明(三)
这篇tkinter控件使用教程是最后一 一、TreeView 属性/事件描述代码实例columns列名,用于设置树形视图的列tree["columns"] ("姓名", "年龄", "性别")column列的属性,包括列名、宽度等tree.column("姓名…...
Electron 打包自定义NSIS脚本为安装向导增加自定义页面增加输入框
Electron 打包工具有很多,如Electron-build、 Electron Forge 等,这里使用Electron-build,而Electron-build使用了nsis组件来创建安装向导,默认情况nsis安装向导不能自定义安装向导界面,但是nsis提供了nsis脚本可以扩展…...
Idea2023创建Servlet项目
① Java EE 只是一个抽象的规范,具体实现称为应用服务器。 ② Java EE 只需要两个包 jsp-api.jar 和 servlet-api.jar,而这两个包是没有官方版本的。也就是说,Java 没有提供这两个包,只提供了一个规范。那么这两个包是谁提供的…...
Day57:WEB攻防-SSRF服务端请求Gopher伪协议无回显利用黑白盒挖掘业务功能点
目录 SSRF-原理&挖掘&利用&修复 SSRF无回显解决办法 SSRF漏洞挖掘 SSRF协议利用 http:// (常用) file:/// (常用) dict:// (常用) sftp:// ldap:// tftp:// gopher:// (…...
【Qt】使用Qt实现Web服务器(十):前端基础
1、简述 本人对HTML元素不熟悉,利用QtWebApp加载静态页面来熟悉下HTML元素。 2、测试代码 # a)main中创建 HttpListener new HttpListener(listenerSettings,new RequestMapper(&app),&app);#...
网站的下载链接怎么做/网站服务器一年的费用
场景:我们想要在php7扩展中调用用户自定的类中的方法,而且方法有多个参数,找到以下方 法,没有看到可以超过两个参数的方法。所以一直向下查找,发现zend_call_method调用的 zend_call_function,但是并非只能…...
网络维护好学吗/网站页面优化方案
GotW #04 Class Mechanics 著者:Herb Sutter 翻译:kingofark [声明]:本文内容取自www.gotw.ca网站上的Guru of the Week栏目,其著作权归原著者本人所有。译者kingofark在未经原著者本人同意的情况下翻译本文。本翻译内容仅供…...
好看的网站页面/深圳经济最新新闻
1.冯诺依曼体系结构 输入设备:键盘,网卡等输出设备:显示器等存储器:进行中间数据缓冲运算器:进行数据运算控制器:进行设备控制 所有的设备都是围绕存储器工作的(CPU控制器运算器)&am…...
网站怎么做自己站长/线上营销渠道主要有哪些
关注云报洞察深一度“浪潮存储,要争取早日成为中国存储市场第一!”浪潮信息总裁彭震在近日举行的IDTC2021浪潮存储数据科技峰会上的一席话,立刻引起了线上线下参会者的热烈反响。浪潮信息总裁 彭震是不是感觉,类似的话曾经在什么场…...
西安给公司做网站/站长seo软件
文章目录1. AspectJ1.1 什么是AspectJ1.2 基于AspectJ实现AOP操作2. Spring实现AOP2.1 基于注解2.1.1 完全注解开发2.2 基于配置文件1. AspectJ Spirng框架一般都是基于AspectJ实现AOP操作 1.1 什么是AspectJ AspectJ不是Spring组成部分,独立于AOP框架࿰…...
wordpress商业主题分享/武汉关键词seo排名
主要内容 线程概念 创建线程的两个方法 实现runnable()接口 重写Thread类中方法 两种方法的区别创建线程与方法调用区别 停止线程的两个方法 interrupt()强制打断 引入变量boolean flag 控制Thread类常用方法 sleep() interrupt() join() yield() setPriority(int newPriority)…...