线程池--简单版本和复杂版本
目录
一、引言
二、线程池头文件介绍
三、简单版本线程池
1.创建线程池
2.添加任务到线程池
3.子线程执行回调函数
4.摧毁线程池
5.简单版线程池流程分析
四、复杂版本线程池
1.结构体介绍
2.主线程
3.子线程
4.管理线程
一、引言
多线程版服务器一个客户端就需要创建一个线程! 若客户端太多, 显然不太
合适.
什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁的创建和销
毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容
忽视,这时也是线程池该出场的机会了。如果线程创建和销毁时间相比任务执行
时间可以忽略不计,则没有必要使用线程池了。
实现的时候类似于生产者和消费
线程池和任务池
线程池 | 任务池 | |
定义 | 线程池是一组可重复使用的线程的集合 | 任务池是一组待执行的任务的集合 |
任务管理 | 线程池负责管理线程的生命周期,包括线程的创建、调度、执行和销毁等。 | 务池负责管理任务的生命周期,包括任务的创建、调度、执行和销毁等 |
并发控制 | 线程池可以根据需要动态调整线程的数量,可以根据系统负载、任务数量等进行调度。 | 任务池可以根据需要动态调整任务的执行顺序和并发度,可以根据任务的优先级、依赖关系等进行调度 |
资源利用 | 线程池可以重复利用线程,避免了频繁创建和销毁线程的开销,提高了系统的性能。 | 任务池可以将多个任务分配给少量的线程执行,可以更有效地利用系统资源 |
使用场景 | 线程池适用于需要管理大量并发任务的场景,例如并发计算、IO操作等 | 任务池适用于需要管理大量独立任务的场景,例如并发请求处理、消息队列等 |
主线程负责向任务池添加任务,而子线程负责从任务池中取出任务并执行。任务池的作用是存放待执行的任务,每个任务都是一个结构体,其中包含了一个回调函数。尽管子线程执行的代码可以是相同的,但是它们从任务池中获取的任务元素是不同的,因此执行的回调函数也会有所不同。通过这种方式,可以实现子线程执行不同的任务,提高程序的并发性和执行效率。
线程相关函数
1. pthread_create:创建线程的函数。
2. pthread_detach:用于将线程分离。分离线程在终止时会自动释放资源,无需其他线程与之进行连接。
3. pthread_attr_t:线程属性的数据类型,用于存储线程的属性信息。
4. pthread_attr_init:用于初始化pthread_attr_t对象,将其设置为默认属性。初始化后,可以使用其他pthread_attr_*函数修改属性。
5. pthread_attr_setdetachstate:用于设置线程的分离状态属性。分离状态可以是PTHREAD_CREATE_JOINABLE(可连接状态)或PTHREAD_CREATE_DETACHED(分离状态)。可连接状态的线程可以通过pthread_join与其他线程连接,而分离状态的线程在终止时会自动释放资源。
6. pthread_exit:用于终止调用线程。可以在线程执行的任何位置调用pthread_exit。当线程调用pthread_exit时,它的资源会自动释放,并将控制返回给父线程。
互斥锁相关函数
pthread_mutex_init:初始化互斥锁。使用该函数可以初始化一个互斥锁对象,设置互斥锁的属性。
pthread_mutex_destroy:销毁互斥锁。使用该函数可以销毁一个互斥锁对象,释放相关资源。
pthread_mutex_lock:加锁。使用该函数可以将互斥锁加锁,如果互斥锁已经被其他线程锁定,则当前线程会被阻塞,直到互斥锁被解锁。
pthread_mutex_trylock:尝试加锁。使用该函数可以尝试将互斥锁加锁,如果互斥锁已经被其他线程锁定,则该函数会立即返回一个错误码。
pthread_mutex_unlock:解锁。使用该函数可以将互斥锁解锁,允许其他线程对互斥锁进行加锁操作。
pthread_mutexattr_init:初始化互斥锁属性对象。使用该函数可以初始化一个互斥锁属性对象,设置互斥锁的属性。
pthread_mutexattr_destroy:销毁互斥锁属性对象。使用该函数可以销毁一个互斥锁属性对象,释放相关资源。
pthread_mutexattr_settype:设置互斥锁类型。使用该函数可以设置互斥锁的类型,包括普通锁、递归锁等。
pthread_mutexattr_gettype:获取互斥锁类型。使用该函数可以获取互斥锁的类型。
若任务池已满,主线程应该阻塞等待子线程处理任务,此时主线程需要阻塞等待
若任务池空了,子线程应该阻塞等待,等待主线程往任务池里面添加任务
pthread_cond_wait函数用于等待条件变量的信号。当一个线程调用pthread_cond_wait时,它会释放当前持有的互斥锁,并进入等待状态,直到收到与该条件变量相关的信号(通常是由其他线程调用pthread_cond_signal发送的)。一旦收到信号,该线程会重新获得互斥锁,并继续执行。
pthread_cond_signal函数用于发送条件变量的信号。当一个线程调用pthread_cond_signal时,它会唤醒等待该条件变量的一个线程(如果有多个线程在等待,则唤醒其中一个)。被唤醒的线程会重新获得互斥锁,并继续执行。
二、线程池头文件介绍
#ifndef _THREADPOOL_H
#define _THREADPOOL_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>typedef struct _PoolTask
{int tasknum;//模拟任务编号void *arg;//回调函数参数void (*task_func)(void *arg);//任务的回调函数
}PoolTask ;typedef struct _ThreadPool
{int max_job_num;//最大任务个数int job_num;//实际任务个数,小于等于max_job_numPoolTask *tasks;//任务队列数组 int job_push;//入队位置,在这个地方添加任务int job_pop;// 出队位置int thr_num;//线程池内线程个数pthread_t *threads;//线程池内线程数组int shutdown;//是否关闭线程池pthread_mutex_t pool_lock;//线程池的锁pthread_cond_t empty_task;//任务队列为空的条件pthread_cond_t not_empty_task;//任务队列不为空的条件}ThreadPool; //线程池void create_threadpool(int thrnum,int maxtasknum);//创建线程池--thrnum 代表线程个数,maxtasknum 最大任务个数
void destroy_threadpool(ThreadPool *pool);//摧毁线程池
void addtask(ThreadPool *pool);//添加任务到线程池
void taskRun(void *arg);//任务回调函数#endif
三、简单版本线程池
1.创建线程池
//创建线程池
void create_threadpool(int thrnum,int maxtasknum)
{printf("begin call %s-----\n",__FUNCTION__);thrPool = (ThreadPool*)malloc(sizeof(ThreadPool));thrPool->thr_num = thrnum;thrPool->max_job_num = maxtasknum;thrPool->shutdown = 0;//是否摧毁线程池,1代表摧毁thrPool->job_push = 0;//任务队列添加的位置thrPool->job_pop = 0;//任务队列出队的位置thrPool->job_num = 0;//初始化的任务个数为0thrPool->tasks = (PoolTask*)malloc((sizeof(PoolTask)*maxtasknum));//申请最大的任务队列//初始化锁和条件变量pthread_mutex_init(&thrPool->pool_lock,NULL);pthread_cond_init(&thrPool->empty_task,NULL);pthread_cond_init(&thrPool->not_empty_task,NULL);int i = 0;thrPool->threads = (pthread_t *)malloc(sizeof(pthread_t)*thrnum);//申请n个线程id的空间pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);for(i = 0;i < thrnum;i++){pthread_create(&thrPool->threads[i],&attr,thrRun,(void*)thrPool);//创建多个线程}//printf("end call %s-----\n",__FUNCTION__);
}
2.添加任务到线程池
void addtask(ThreadPool *pool)
{pthread_mutex_lock(&pool->pool_lock); // 加锁,确保线程池的任务队列安全访问while(pool->max_job_num <= pool->job_num) // 当任务队列已满时,等待任务队列有空闲位置{pthread_cond_wait(&pool->empty_task,&pool->pool_lock); // 等待任务队列有空闲位置}int taskpos = (pool->job_push++)%pool->max_job_num; // 计算任务在任务队列中的位置pool->tasks[taskpos].tasknum = beginnum++; // 设置任务的编号pool->tasks[taskpos].arg = (void*)&pool->tasks[taskpos]; // 设置任务的参数pool->tasks[taskpos].task_func = taskRun; // 设置任务的函数指针pool->job_num++; // 增加任务数量pthread_mutex_unlock(&pool->pool_lock); // 解锁pthread_cond_signal(&pool->not_empty_task); // 通知线程池有新的任务可执行
}
3.子线程执行回调函数
void *thrRun(void *arg)
{//printf("begin call %s-----\n",__FUNCTION__);ThreadPool *pool = (ThreadPool*)arg;int taskpos = 0;//任务位置PoolTask *task = (PoolTask *)malloc(sizeof(PoolTask));while(1){//获取任务,先要尝试加锁pthread_mutex_lock(&thrPool->pool_lock);//无任务并且线程池不是要摧毁while(thrPool->job_num <= 0 && !thrPool->shutdown ){//如果没有任务,线程会阻塞pthread_cond_wait(&thrPool->not_empty_task,&thrPool->pool_lock);}if(thrPool->job_num){//有任务需要处理taskpos = (thrPool->job_pop++)%thrPool->max_job_num;//printf("task out %d...tasknum===%d tid=%lu\n",taskpos,thrPool->tasks[taskpos].tasknum,pthread_self());//为什么要拷贝?避免任务被修改,生产者会添加任务memcpy(task,&thrPool->tasks[taskpos],sizeof(PoolTask));task->arg = task;thrPool->job_num--;//task = &thrPool->tasks[taskpos];pthread_cond_signal(&thrPool->empty_task);//通知生产者}if(thrPool->shutdown){//代表要摧毁线程池,此时线程退出即可//pthread_detach(pthread_self());//临死前分家pthread_mutex_unlock(&thrPool->pool_lock);free(task);pthread_exit(NULL);}//释放锁pthread_mutex_unlock(&thrPool->pool_lock);task->task_func(task->arg);//执行回调函数}//printf("end call %s-----\n",__FUNCTION__);
}
4.摧毁线程池
//摧毁线程池
void destroy_threadpool(ThreadPool *pool)
{pool->shutdown = 1;//开始自爆pthread_cond_broadcast(&pool->not_empty_task);//诱杀 int i = 0;for(i = 0; i < pool->thr_num ; i++){pthread_join(pool->threads[i],NULL);}pthread_cond_destroy(&pool->not_empty_task);pthread_cond_destroy(&pool->empty_task);pthread_mutex_destroy(&pool->pool_lock);free(pool->tasks);free(pool->threads);free(pool);
}
5.简单版线程池流程分析
四、复杂版本线程池
1.结构体介绍
typedef
struct
{
void
*(*function)(
void
*);
/* 函数指针,回调函数 */
void
*arg;
/* 上面函数的参数 */
} threadpool_task_t;
/* 各子线程任务结构体 */
/* 描述线程池相关信息 */
struct
threadpool_t
{
pthread_mutex_t lock;
/* 用于锁住本结构体 */
pthread_mutex_t thread_counter;
/* 记录忙状态线程个数de琐 -- busy_thr_num */
pthread_cond_t queue_not_full;
/* 当任务队列满时,添加任务的线程阻塞,等待此条件变量 */
pthread_cond_t queue_not_empty;
/* 任务队列里不为空时,通知等待任务的线程 */
pthread_t *threads;
/* 存放线程池中每个线程的tid。数组 */
pthread_t adjust_tid;
/* 存管理线程tid */
threadpool_task_t *task_queue;
/* 任务队列(数组首地址) */
int
min_thr_num;
/* 线程池最小线程数 */
int
max_thr_num;
/* 线程池最大线程数 */
int
live_thr_num;
/* 当前存活线程个数 */
int
busy_thr_num;
/* 忙状态线程个数 */
int
wait_exit_thr_num;
/* 要销毁的线程个数 */
int
queue_front;
/* task_queue队头下标 */
int
queue_rear;
/* task_queue队尾下标 */
int
queue_size;
/* task_queue队中实际任务数 */
int
queue_max_size;
/* task_queue队列可容纳任务数上限 */
int
shutdown;
/* 标志位,线程池使用状态,true或false */
};
2.主线程
//threadpool_create(3,100,100);
threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size)
{int i;threadpool_t *pool = NULL;do{if((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL) { printf("malloc threadpool fail");break; /*跳出do while*/}pool->min_thr_num = min_thr_num;pool->max_thr_num = max_thr_num;pool->busy_thr_num = 0;pool->live_thr_num = min_thr_num; /* 活着的线程数 初值=最小线程数 */pool->wait_exit_thr_num = 0;pool->queue_size = 0; /* 有0个产品 */pool->queue_max_size = queue_max_size;pool->queue_front = 0;pool->queue_rear = 0;pool->shutdown = false; /* 不关闭线程池 *//* 根据最大线程上限数, 给工作线程数组开辟空间, 并清零 */pool->threads = (pthread_t *)malloc(sizeof(pthread_t)*max_thr_num); if (pool->threads == NULL) {printf("malloc threads fail");break;}memset(pool->threads, 0, sizeof(pthread_t)*max_thr_num);/* 队列开辟空间 */pool->task_queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t)*queue_max_size);if (pool->task_queue == NULL) {printf("malloc task_queue fail\n");break;}/* 初始化互斥琐、条件变量 */if (pthread_mutex_init(&(pool->lock), NULL) != 0|| pthread_mutex_init(&(pool->thread_counter), NULL) != 0|| pthread_cond_init(&(pool->queue_not_empty), NULL) != 0|| pthread_cond_init(&(pool->queue_not_full), NULL) != 0){printf("init the lock or cond fail\n");break;}//启动工作线程pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);for (i = 0; i < min_thr_num; i++) {pthread_create(&(pool->threads[i]), &attr, threadpool_thread, (void *)pool);/*pool指向当前线程池*/printf("start thread 0x%x...\n", (unsigned int)pool->threads[i]);}//创建管理者线程pthread_create(&(pool->adjust_tid), &attr, adjust_thread, (void *)pool);return pool;} while (0);/* 前面代码调用失败时,释放poll存储空间 */threadpool_free(pool);return NULL;
}
/* 向线程池中 添加一个任务 */
//threadpool_add(thp, process, (void*)&num[i]); /* 向线程池中添加任务 process: 小写---->大写*/int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg)
{pthread_mutex_lock(&(pool->lock));/* ==为真,队列已经满, 调wait阻塞 */while ((pool->queue_size == pool->queue_max_size) && (!pool->shutdown)) {pthread_cond_wait(&(pool->queue_not_full), &(pool->lock));}if (pool->shutdown) {pthread_cond_broadcast(&(pool->queue_not_empty));pthread_mutex_unlock(&(pool->lock));return 0;}/* 清空 工作线程 调用的回调函数 的参数arg */if (pool->task_queue[pool->queue_rear].arg != NULL) {pool->task_queue[pool->queue_rear].arg = NULL;}/*添加任务到任务队列里*/pool->task_queue[pool->queue_rear].function = function;pool->task_queue[pool->queue_rear].arg = arg;pool->queue_rear = (pool->queue_rear + 1) % pool->queue_max_size; /* 队尾指针移动, 模拟环形 */pool->queue_size++;/*添加完任务后,队列不为空,唤醒线程池中 等待处理任务的线程*/pthread_cond_signal(&(pool->queue_not_empty));pthread_mutex_unlock(&(pool->lock));return 0;
}
3.子线程
/* 线程池中各个工作线程 */
void *threadpool_thread(void *threadpool)
{threadpool_t *pool = (threadpool_t *)threadpool;threadpool_task_t task;while (true) {/* Lock must be taken to wait on conditional variable *//*刚创建出线程,等待任务队列里有任务,否则阻塞等待任务队列里有任务后再唤醒接收任务*/pthread_mutex_lock(&(pool->lock));/*queue_size == 0 说明没有任务,调 wait 阻塞在条件变量上, 若有任务,跳过该while*/while ((pool->queue_size == 0) && (!pool->shutdown)) { printf("thread 0x%x is waiting\n", (unsigned int)pthread_self());pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));//暂停到这/*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/if (pool->wait_exit_thr_num > 0) {pool->wait_exit_thr_num--;/*如果线程池里线程个数大于最小值时可以结束当前线程*/if (pool->live_thr_num > pool->min_thr_num) {printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());pool->live_thr_num--;pthread_mutex_unlock(&(pool->lock));//pthread_detach(pthread_self());pthread_exit(NULL);}}}/*如果指定了true,要关闭线程池里的每个线程,自行退出处理---销毁线程池*/if (pool->shutdown) {pthread_mutex_unlock(&(pool->lock));printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());//pthread_detach(pthread_self());pthread_exit(NULL); /* 线程自行结束 */}/*从任务队列里获取任务, 是一个出队操作*/task.function = pool->task_queue[pool->queue_front].function;task.arg = pool->task_queue[pool->queue_front].arg;pool->queue_front = (pool->queue_front + 1) % pool->queue_max_size; /* 出队,模拟环形队列 */pool->queue_size--;/*通知可以有新的任务添加进来*/pthread_cond_broadcast(&(pool->queue_not_full));/*任务取出后,立即将 线程池琐 释放*/pthread_mutex_unlock(&(pool->lock));/*执行任务*/printf("thread 0x%x start working\n", (unsigned int)pthread_self());pthread_mutex_lock(&(pool->thread_counter)); /*忙状态线程数变量琐*/pool->busy_thr_num++; /*忙状态线程数+1*/pthread_mutex_unlock(&(pool->thread_counter));(*(task.function))(task.arg); /*执行回调函数任务*///task.function(task.arg); /*执行回调函数任务*//*任务结束处理*/printf("thread 0x%x end working\n", (unsigned int)pthread_self());pthread_mutex_lock(&(pool->thread_counter));pool->busy_thr_num--; /*处理掉一个任务,忙状态数线程数-1*/pthread_mutex_unlock(&(pool->thread_counter));}pthread_exit(NULL);
}
4.管理线程
/* 管理线程 */
void *adjust_thread(void *threadpool)
{int i;threadpool_t *pool = (threadpool_t *)threadpool;while (!pool->shutdown) {sleep(DEFAULT_TIME); /*定时 对线程池管理*/pthread_mutex_lock(&(pool->lock));int queue_size = pool->queue_size; /* 关注 任务数 */int live_thr_num = pool->live_thr_num; /* 存活 线程数 */pthread_mutex_unlock(&(pool->lock));pthread_mutex_lock(&(pool->thread_counter));int busy_thr_num = pool->busy_thr_num; /* 忙着的线程数 */pthread_mutex_unlock(&(pool->thread_counter));/* 创建新线程 算法: 任务数大于最小线程池个数, 且存活的线程数少于最大线程个数时 如:30>=10 && 40<100*/if (queue_size >= MIN_WAIT_TASK_NUM && live_thr_num < pool->max_thr_num) {pthread_mutex_lock(&(pool->lock)); int add = 0;/*一次增加 DEFAULT_THREAD 个线程*/for (i = 0; i < pool->max_thr_num && add < DEFAULT_THREAD_VARY&& pool->live_thr_num < pool->max_thr_num; i++) {if (pool->threads[i] == 0 || !is_thread_alive(pool->threads[i])) {pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);add++;pool->live_thr_num++;}}pthread_mutex_unlock(&(pool->lock));}/* 销毁多余的空闲线程 算法:忙线程X2 小于 存活的线程数 且 存活的线程数 大于 最小线程数时*/if ((busy_thr_num * 2) < live_thr_num && live_thr_num > pool->min_thr_num) {/* 一次销毁DEFAULT_THREAD个线程, 隨機10個即可 */pthread_mutex_lock(&(pool->lock));pool->wait_exit_thr_num = DEFAULT_THREAD_VARY; /* 要销毁的线程数 设置为10 */pthread_mutex_unlock(&(pool->lock));for (i = 0; i < DEFAULT_THREAD_VARY; i++) {/* 通知处在空闲状态的线程, 他们会自行终止*/pthread_cond_signal(&(pool->queue_not_empty));}}}return NULL;
}
相关文章:

线程池--简单版本和复杂版本
目录 一、引言 二、线程池头文件介绍 三、简单版本线程池 1.创建线程池 2.添加任务到线程池 3.子线程执行回调函数 4.摧毁线程池 5.简单版线程池流程分析 四、复杂版本线程池 1.结构体介绍 2.主线程 3.子线程 4.管理线程 一、引言 多线程版服务器一个客户端就需要…...

docker进阶
文章目录 docker 进阶Part1 常用命令总结docker version 查看docker客户端和服务端信息docker info 查看更加详细信息docker images 列出所有镜像基本用法常用选项 docker search 搜索镜像基本用法示例用法 docker pull 拉取镜像基本用法示例用法 docker rmi 删除镜像基本用法示…...

Unity HoloLens 2 应用程序发布
设置3D 启动器画面,glb格式的模型 VS中可以直接生成所有大小的图标...

3D RPG Course | Core 学习日记三:Navigation智能导航地图烘焙
前言 前面我们已经绘制好了一个简单的地图场景,现在我们需要使用Navigation给地图做智能导航,以实现AI自动寻路,以及设置地图的可行走区域以及不可行走区域,Navigation的基础知识、原理、用法在Unity的官方文档,以及网…...
Linux 启用本地ISO作为软件源
环境:sle12sp5 (open SUSE) 1、禁用现有的源 查看源:sle12sp5 zypper lr -u ➜ sle12sp5 zypper lr -u Repository priorities are without effect. All enabled repositories share the same prior…...

SpringCloud-Alibaba-Nacos2.0.4
SpringCloud-Alibaba-Nacos2.0.4 SpringCloud Alibaba版本选择(截止到2023年3月12日) Spring Cloud Alibaba VersionSpring Cloud VersionSpring Boot Version2021.0.4.0*Spring Cloud 2021.0.42.6.11 SpringCloud Alibaba-2021.0.4.0组件版本关系 S…...
docker运行镜像相关配置文件
Dockerfile 文件配置 FROM anapsix/alpine-java:8_server-jre_unlimitedMAINTAINER Lion LiRUN mkdir -p /data/sydatasource/logs \/data/sydatasource/temp \/data/skywalking/agentWORKDIR /data/sydatasourceENV SERVER_PORT8220EXPOSE ${SERVER_PORT}ENV TZAsia/Shanghai …...
引擎系统设计思路 - 用户态与系统态隔离
用户态与系统态隔离: a. 外部用户侧的对象或者逻辑,在外部创建使用。内部系统侧的对象或者逻辑,在内部创建使用。 b. 用户状态下对内部系统的操作要立即响应,但是具体如何实际执行系统内部的机制,则是异步并行的。因为…...

致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC]
文章目录 致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用…...

轻量应用服务器有什么优势?如何评价亚马逊云科技轻量应用服务器?
什么是轻量应用服务器? 随着如今各行各业对云计算的需求越来越多,云服务器也被越来越多的企业所广泛采用。其中,轻量应用服务器是一种简单、高效、可靠的云计算服务,能够为开发人员、企业和个人提供轻量级的虚拟专用服务器&#…...
python的日志模块学习记录
logging模块是Python的内置日志模块满足日常需要 使用方法 *** 1.导入*** import logging from logging import config*** 2.配置日志配置项(基本配置一般不能满足需要,一般使用字典配置如下)*** # 日志基本配置方法,一般不能满足需要 logging.basic…...
【java】redisTemplate mock时报空指针
原方法: Boolean locked redisTemplate.opsForValue().setIfAbsent(redisKey, "", 400, TimeUnit.SECONDS);mock方法 mock方法: 需要每个函数都mock。 Mock RedisTemplate redisTemplate;... ValueOperations<String, String> value…...

Hadoop PseudoDistributed Mode 伪分布式
Hadoop PseudoDistributed Mode 伪分布式加粗样式 hadoop101hadoop102hadoop103192.168.171.101192.168.171.102192.168.171.103namenodesecondary namenoderecource managerdatanodedatanodedatanodenodemanagernodemanagernodemanagerjob historyjob logjob logjob log 1. …...
个人职业规划
职业规划 软件体系结构 内容 组件 关系 视图 技术 抽象 封装 信息隐藏 模块化 事务分离 耦合和内聚 充分性、完整性和原始性 策略和实现的分离 接口和实现的分离 单一引用点 分而治之 结构 层 管道和过滤器 黑板 系统 分布式系统 代理者 交互式系统 …...

Linux | 如何保持 SSH 会话处于活动状态
在远程服务器管理和安全数据传输中,SSH(Secure Shell)是不可或缺的工具。然而,它的便利性和安全性有时会因常见的问题而受到损害:冻结 SSH 会话。 此外,session 的突然中断可能会导致工作丢失、项目延迟和无…...

树结构及其算法-二叉树节点的插入
目录 树结构及其算法-二叉树节点的插入 C代码 树结构及其算法-二叉树节点的插入 二叉树节点插入的情况和查找相似,重点是插入后仍要保持二叉查找树的特性。如果插入的节点已经在二叉树中,就没有插入的必要了,如果插入的值不在二叉树中&…...

JVM 分代垃圾回收过程
堆空间划分了代: 年轻代(Young Generation)分为 eden 和 Survivor 两个区,Survivor 又分为2个均等的区,S0 和 S1。 首先,新对象都分配到年轻代的 eden 空间,Survivor 刚开始是空的。 当 eden …...

【C++】 常对象与常函数
常函数: 成员函数后加const后我们称为这个函数为常函数常函数内不可以修改成员属性成员属性声明时加关键字mutable后,在常函数中依然可以修改 常对象: 声明对象前加const称该对象为常对象常对象只能调用常函数 一、this指针本质 this指针…...

Elasticsearch 集群分片出现 unassigned 其中一种原因详细还原
🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹…...

Java调用HTTPS接口,绕过SSL认证
1:说明 网络编程中,HTTPS(Hypertext Transfer Protocol Secure)是一种通过加密的方式在计算机网络上进行安全通信的协议。网络传输协议,跟http相比更安全,因为他加上了SSL/TLS协议来加密通信内容。 Java调…...
spring:继承接口FactoryBean获取bean实例
spring框架提供接口FactoryBean获取bean实例。 实现步骤: 实现接口FactoryBean。 在xml文件中配置实现接口FactoryBean的类。 调用接口FactoryBean中方法getObject,获取bean实例。 实现接口类 package com.itheima.factory;import org.springframework…...

RockyLinux9.6搭建k8s集群
博主介绍:✌全网粉丝5W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验…...

Java 大视界 -- 基于 Java 的大数据分布式计算在蛋白质组学数据分析中的加速与优化(255)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
Q: dify前端使用哪些开发框架?
【回到目录】~~~~【回到问题集】 Q: dify前端使用哪些开发框架? A: 通过查看Readme.md,可以了解到使用以下框架 1. [Next.js] (https://nextjs.org/) React Framework 2. Node.js > v22.11.x 3. pnpm v10.x 4. Storybook UI component development 4. Je…...

黄晓明新剧《潜渊》定档 失忆三面间谍开启谍战新维度
据悉,黄晓明领衔主演的谍战剧《潜渊》已于近日正式定档6月9日,该剧以“失忆三面间谍”梁朔为核心,打破传统谍战剧的框架和固有角度,以一种特别的视角将悬疑感推向极致。剧中,梁朔因头部受伤失去记忆,陷入身…...

汽车安全体系:FuSa、SOTIF、Cybersecurity 从理论到实战
汽车安全:功能安全(FuSa)、预期功能安全(SOTIF)与网络安全(Cybersecurity) 从理论到实战的安全体系 引言:自动驾驶浪潮下的安全挑战 随着自动驾驶技术从L2向L4快速演进,汽车安全正从“机械可靠…...

Excel处理控件Aspose.Cells教程:使用 C# 在 Excel 中创建组合图表
可视化项目时间线对于有效规划和跟踪至关重要。在本篇教程中,您将学习如何使用 C# 在 Excel 中创建组合图。只需几行代码,即可自动生成动态、美观的组合图。无论您是在构建项目管理工具还是处理内部报告,本指南都将向您展示如何将任务数据转换…...

明基编程显示器终于有优惠了,程序员快来,错过等一年!
最近618的活动已经陆续开始了,好多人说这是买数码产品的好时候,作为一名资深程序员,我做了不少功课,决定给自己升级办公设备,入手明基 RD 系列的显示器,这是市面上首家专注于我们程序员痛点和需求的产品&am…...
JavaScript中的正则表达式:文本处理的瑞士军刀
JavaScript中的正则表达式:文本处理的瑞士军刀 在编程世界中,正则表达式(Regular Expression,简称RegExp)被誉为“文本处理的瑞士军刀”。它能够高效地完成字符串匹配、替换、提取和验证等任务。无论是前端开发中的表…...

Linux --TCP协议实现简单的网络通信(中英翻译)
一、什么是TCP协议 1.1 、TCP是传输层的协议,TCP需要连接,TCP是一种可靠性传输协议,TCP是面向字节流的传输协议; 二、TCPserver端的搭建 2.1、我们最终好实现的效果是 客户端在任何时候都能连接到服务端,然后向服务…...