JDK17 线程池 ThreadPoolExecutor
文章目录
- 线程池
- ThreadPoolExecutor
- 状态
- 向线程池添加任务 execute
- Worker
- 线程池新建工作线程 addWorker
- 拒绝策略
线程池
线程池将创建线程和使用线程解耦。优点是
- 避免重复创建和销毁线程,降低资源消耗。
- 任务不用等待创建线程的时间,提高响应速度。
- 统一管理线程,避免线程数量过多,造成OOM。保障系统稳定运行。
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();// 以上是参数校验this.corePoolSize = corePoolSize; // 核心线程数量this.maximumPoolSize = maximumPoolSize; // 最大线程数量this.workQueue = workQueue; // 任务队列this.keepAliveTime = unit.toNanos(keepAliveTime); // 非核心线程的存活时间this.threadFactory = threadFactory; // 线程工厂类this.handler = handler; // 拒绝策略执行器
}
corePoolSize
是核心线程数,默认核心线程不会被销毁。
maximumPoolSize
大等于corePoolSize
,如果核心线程不足以任务并且任务队列已满,那么会新建非核心线程处理任务。如果线程数达到最大线程数,那么不执行任务,而是执行拒绝策略。非核心线程会被销毁,它们如果在keepAliveTime
存活时间内没有收到任务则会被销毁。
workQueue
任务队列存放工作线程还没来得及处理的任务。
threadFactory
是新建线程的工厂类,定义线程的名称,优先级以及是否是守护线程。默认用Executors.DefaultThreadFactory
。
拒绝策略执行器是线程池无法接收新线程(可能原因1. 线程池已被关闭 2. 工作线程数已经是最大线程且任务队列满了)时执行的方法。默认用ThreadPoolExecutor#AbortPolicy
.
最少只需要填四个参数,corePoolSize, maximumPoolSize, workQueue, keepAliveTime
,就可以创建线程池。
这里一张图
状态
ctl
表示线程池状态,它的类型是AtomicInteger
,AtomicInteger
类实现原子性操作int
值。ctl
的前3位是线程池状态,通过runStateOf()
获得。ctl
的后29位是运行数,workerCountOf()
方法可得。
单独看状态,可以为分为5种。RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED
。其中RUNNING<0
,表示线程池正常运行。SHUTDOWN =0
表示不接收新任务,但会执行完已有任务。STOP=1
表示不接收新任务,且不执行已有任务。TIDYING=2
表示线程池已清空。TERMINATED =3
表示线程池已终止。
这里1张图。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // 线程池状态
private static final int COUNT_BITS = Integer.SIZE - 3; // 29
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1; // 000(29个1)
private static final int RUNNING = -1 << COUNT_BITS; // 111(29个0)
private static final int SHUTDOWN = 0 << COUNT_BITS; // 0
private static final int STOP = 1 << COUNT_BITS; // 001(29个0)
private static final int TIDYING = 2 << COUNT_BITS; // 010(29个0)
private static final int TERMINATED = 3 << COUNT_BITS; // 010(29个0)private static int runStateOf(int c) { return c & ~COUNT_MASK; }
private static int workerCountOf(int c) { return c & COUNT_MASK; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
向线程池添加任务 execute
public void execute(Runnable command) { // command是线程池接收的任务if (command == null) // 参数校验throw new NullPointerException();int c = ctl.get(); // 线程池状态if (workerCountOf(c) < corePoolSize) { // 如果工作线程数少于核心线程数if (addWorker(command, true)) // 增加核心工作线程return;c = ctl.get();}// 运行至此,当前线程数不少于核心线程数// isRunning()返回线程池是否是RUNNING状态,是则true// workQueue.offer(command)) 将任务阻塞地加入队列if (isRunning(c) && workQueue.offer(command)) {// 运行至此,任务已进入阻塞队列int recheck = ctl.get(); // 如果线程池状态不是RUNNING,取消任务,执行拒绝策略if (! isRunning(recheck) && remove(command))reject(command);// 如果工作线程数是0,启动一个非核心工作线程else if (workerCountOf(recheck) == 0)addWorker(null, false);}// 运行至此,没有成功加入阻塞队列// 添加非核心线程执行任务else if (!addWorker(command, false))reject(command);
}
addWorker(command, true)
表示线程池增加核心工作线程,addWorker(command, false)
表示增加非核心线程。execute()
方法的流程图如下
直接上图。
Worker
想理解addWorker()
方法,得先理解Worker
类。Worker
封装Thread
类型变量和Runnable
类型变量。this.thread = getThreadFactory().newThread(this);
表示thread
是工作线程,执行Worker
类的run()
方法,后面addWork()
方法会执行worker.start()
真正开启新线程。后者表示任务。
Worker
类继承AbstractQueuedSynchronizer
是为了实现不可重入独占锁, state
是Worker
类的状态,表示当前工作线程是否正在执行任务。除了工作线程执行线程池任务,调用线程池的多个线程可能也会操作线程池任务(比如STOP
状态中断所有任务),为了防止线程安全问题,因此加锁。
private final class Workerextends AbstractQueuedSynchronizerimplements Runnable
{final Thread thread; // 工作线程Runnable firstTask; // 线程池任务Worker(Runnable firstTask) {setState(-1); // inhibit interrupts until runWorkerthis.firstTask = firstTask;this.thread = getThreadFactory().newThread(this);}public void run() {runWorker(this);}
}
runWorker(Worker w)
定义工作线程声明周期内的操作。只有task.run();
表示工作线程执行客户端发送给线程池的任务,其余的源码表示工作线程的其他操作。三层try-catch
,第一层表示工作线程从任务队列拉取任务。第二层表示工作线程执行环境任务beforeExecute(), afterExecute()
。第三层表示工作线程执行任务。
final void runWorker(Worker w) {Thread wt = Thread.currentThread(); // 当前线程是工作线程,不是调用线程池的客户端线程Runnable task = w.firstTask;w.firstTask = null;w.unlock(); // allow interruptsboolean completedAbruptly = true;// 第一层try结束代表当前工作线程要被销毁try {// task != null 表示新建工作线程时就分配任务// getTask() 表示阻塞地从任务队列拉取任务while (task != null || (task = getTask()) != null) {w.lock();// 至此,工作线程获得不可重入独占锁// (runStateAtLeast(ctl.get(), STOP)表示线程池的状态是`STOP,TIDYING,TERMINATED`,线程池要求中断任务,中断任务是指设置中断位为true,而不是强制终止任务// 这个if语句的语义是:清除当前线程的中断标记,不干扰执行任务期间的中断状态,除非如果线程池要求中断任务。// 尽可能获取最新线程池状态,因此执行2次(runStateAtLeast(ctl.get(), STOP)if ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())wt.interrupt();// 运行至此,正常情况下线程中断位为false,线程池状态为`STOP,TIDYING,TERMINATED`时中断位为true// 第二层try表示工作线程执行本次任务try {beforeExecute(wt, task); // 钩子方法,所有任务公用一个方法体// 第三层try表示执行客户端提交给线程池的任务// `run()`方法不会开启一个新线程try {task.run();afterExecute(task, null);} catch (Throwable ex) {afterExecute(task, ex);throw ex;}} finally {task = null;w.completedTasks++;w.unlock();}}// 运行至此,代表任务没有抛出异常completedAbruptly = false;} finally {// 运行至此,completedAbruptly =true表示任务`task.run()`抛出异常。// 运行至此,工作线程没有获取到任务,如果不符合线程池要求,则销毁这个工作线程processWorkerExit(w, completedAbruptly);}// 至此,工作线程执行完`run()`方法中所有内容,终止该线程
}
工作线程的终结方法processWorkerExit()
。尝试销毁工作线程,并且尝试终止线程池。
decrementWorkerCount()
方法就是将状态ctl
-1,表示工作线程减1.如果任务没有抛出异常,工作线程会在getTask()
方法里执行该方法。
mainLock
是线程池的重入独占锁,保证多个线程调用线程池时线程安全。
private void processWorkerExit(Worker w, boolean completedAbruptly) {if (completedAbruptly) // If abrupt, then workerCount wasn't adjusteddecrementWorkerCount();final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {completedTaskCount += w.completedTasks; // 线程池完成的任务数+=当前工作线程完成的任务数workers.remove(w); // 当前工作线程被移出集合,意味着工作线程被销毁} finally {mainLock.unlock();}tryTerminate(); // 当前线程已被销毁,是否意味着线程池进入非`RUNNING`状态。int c = ctl.get();if (runStateLessThan(c, STOP)) { // true表示:线程池的状态是`RUNNING, SHUTDOWN`,继续执行线程池已有任务if (!completedAbruptly) { // 如果任务没有抛出异常int min = allowCoreThreadTimeOut ? 0 : corePoolSize; // 线程池最小线程数if (min == 0 && ! workQueue.isEmpty()) // 如果最小线程数为0,并且仍要执行任务min = 1; // 最小线程为1if (workerCountOf(c) >= min) // 当前工作线程满足执行任务的要求return; // replacement not needed}// 如果任务抛出异常或者当前工作线程不满足执行任务的要求,新建一个非核心线程,不布置任务addWorker(null, false);}}
tryTerminate()
方法尝试让线程池进入TERMINATED
状态。interruptIdleWorkers()
方法将中断一个空闲的工作线程。
final void tryTerminate() {for (;;) { // 自旋int c = ctl.get();if (isRunning(c) ||runStateAtLeast(c, TIDYING) ||(runStateLessThan(c, STOP) && ! workQueue.isEmpty()))// 运行至此,线程池状态要么是`RUNNING`,要么是`TIDYING, TERMINATED`,要么是`SHUTDOWN`并且任务队列为空// `return;`表示第一种情况和第三种情况下线程池还不能进入终止状态,第二种情况表示不用重复执行下述操作return;// 运行至此,线程池状态不是`RUNNING, TIDYING, TERMINATED`。线程池的状态可能是// 1. `SHUTDOWN`并且任务队列为空// 2. `STOP`,无所谓队列空不空,因为工作线程不执行队列中任务// 第一种情况下可能有非常多工作线程阻塞在`getTask()`方法的`workQueue.take()`/`workQueue.poll()`方法。设置工作线程的标记位后,工作线程会跳出阻塞抛出`InterruptedException`异常。为了防止大规模抛出异常,造成系统不稳定,因此一个一个中断线程。if (workerCountOf(c) != 0) { // Eligible to terminateinterruptIdleWorkers(ONLY_ONE); // 中断一个工作线程return;}// 至此,`SHUTDOWN`满足任务队列为空并且工作线程数为0,`STOP`满足工作线程数为0final ReentrantLock mainLock = this.mainLock;mainLock.lock(); // 获取线程池的重入独占锁try {if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { // true则表示当前线程池状态已经被修改为`TIDYING`,并且工作线程数是0 只有1个线程能返回truetry {terminated(); // 钩子方法,线程池终止方法} finally {ctl.set(ctlOf(TERMINATED, 0));// 至此,终止线程池termination.signalAll();}return;}} finally {mainLock.unlock();}// else retry on failed CAS}
}
线程池新建工作线程 addWorker
Worker
类定义了工作线程的内容。但是创建工作线程是线程池调用addWorker()
方法完成的。retry
循环尝试获取新增线程授权,获得授权后新增工作线程。
private boolean addWorker(Runnable firstTask, boolean core) {retry:for (int c = ctl.get();;) {// Check if queue empty only if necessary.if (runStateAtLeast(c, SHUTDOWN) // 表示线程池非`RUNNING`&& (runStateAtLeast(c, STOP) // 表示线程池为`STOP, TIDYING, TERMINATED`|| firstTask != null // 任务不为空|| workQueue.isEmpty())) // 任务队列为空return false;// 运行至此,没有`return false`,可能情况// 1. 线程池状态为`RUNNING`// 2. 线程池状态为`SHUTDOWN`,并且以下条件满足1个// 1. 任务为空,表示不接收新任务// 2. 任务队列非空,表示仍然执行任队列里的任务for (;;) { // 自旋if (workerCountOf(c)>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))return false; // 工作线程数已经达到阈值了,不创建新线程if (compareAndIncrementWorkerCount(c)) break retry; // 成功使状态`ctl`+1,获得创建新线程许可,跳出循环// 至此,没有获得许可,继续尝试c = ctl.get(); // Re-read ctlif (runStateAtLeast(c, SHUTDOWN))continue retry; // 如果线程池状态不是`RUNNING`,那么重新进入retry循环。如果是`RUNNING`,这个for循环自旋就行。// else CAS failed due to workerCount change; retry inner loop}}boolean workerStarted = false; // 工作线程是否执行`start()`方法,即新建线程boolean workerAdded = false; // 工作线程是否被添加进入工作线程集合Worker w = null;try {w = new Worker(firstTask); // 新建工作线程对象final Thread t = w.thread;if (t != null) {final ReentrantLock mainLock = this.mainLock;mainLock.lock(); // 获取线程池独占锁,为了原子性更新`workers`,`largestPoolSize`。try {// Recheck while holding lock.// Back out on ThreadFactory failure or if// shut down before lock acquired.int c = ctl.get();if (isRunning(c) ||(runStateLessThan(c, STOP) && firstTask == null)) {// 再次判断线程池状态,可能情况// 1. `RUNNING`// 2. `SHUTDOWN`并且任务为空if (t.getState() != Thread.State.NEW)throw new IllegalThreadStateException(); // 线程状态已被篡改,抛出异常workers.add(w);workerAdded = true;int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;}} finally {mainLock.unlock();}if (workerAdded) { // 如果工作线程进入集合,表示线程池已经收录这个`Worker`对象,则可以启动线程t.start(); // 创建新线程workerStarted = true;}}} finally {if (! workerStarted)addWorkerFailed(w);}return workerStarted;
}
如果没有新建工作线程,则执行回退。
private void addWorkerFailed(Worker w) {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {if (w != null)workers.remove(w); // 线程集合删除本工作线程decrementWorkerCount(); // 减少工作线程数tryTerminate(); // 检查线程池是否已经准备进入终止状态} finally {mainLock.unlock();}
}
拒绝策略
JDK17提供四种拒绝策略。可以自定义拒绝策略,定义RejectedExecutionHandler
接口的实现类,重写rejectedExecution()
方法。ThreadPoolExecutor
类的默认拒绝策略是AbortPolicy
。
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
// AbortPolicy 直接抛出异常
public static class AbortPolicy implements RejectedExecutionHandler {public AbortPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException("Task " + r.toString() +" rejected from " +e.toString());}
}
// DiscardPolicy丢弃,不做任何处理
public static class DiscardPolicy implements RejectedExecutionHandler {public DiscardPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
}
// DiscardOldestPolicy尝试丢弃任务队列的头部线程
public static class DiscardOldestPolicy implements RejectedExecutionHandler {public DiscardOldestPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll();e.execute(r);}}
}
// CallerRunsPolicy尝试使用线程池对象的线程处理任务,也就是谁提交任务,谁处理任务。run()方法不会新建线程对象,而是直接本线程执行任务内容
public static class CallerRunsPolicy implements RejectedExecutionHandler {public CallerRunsPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}
}
相关文章:
JDK17 线程池 ThreadPoolExecutor
文章目录 线程池ThreadPoolExecutor状态向线程池添加任务 executeWorker线程池新建工作线程 addWorker 拒绝策略 线程池 线程池将创建线程和使用线程解耦。优点是 避免重复创建和销毁线程,降低资源消耗。任务不用等待创建线程的时间,提高响应速度。统一…...
Dify+Docker
1. 获取代码 直接下载 (1)访问 langgenius/dify: Dify is an open-source LLM app development platform. Difys intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, …...
分布式会话 详解
分布式会话详解 在分布式系统中,用户的会话状态需要在多个服务器或节点之间共享或存储。分布式会话指的是在这种场景下如何管理和存储会话,以便在多个节点上都能正确识别用户状态,从而保证用户体验的一致性。 1. 为什么需要分布式会话 在单…...
Java进阶
Java进阶 java注解 java中注解(Annotation),又称为java标注,是一种特殊的注释,可以添加在包,类,成员变量,方法,参数等内容上面.注解会随同代码编译到字节码文件中,在运行时,可以通过反射机制获取到类中的注解,然后根据不同的注解进行相应的解析. 内置注解 Java 语言中已经定…...
Qt/C++实现帧同步播放器/硬解码GPU绘制/超低资源占用/支持8K16K/支持win/linux/mac/嵌入式/国产OS等
一、前言 首先泼一盆冷水,在不同的电脑上实现完完全全的帧同步理论上是不可能的,市面上所有号称帧同步的播放器,同一台电脑不同拼接视频可以通过合并成一张图片来绘制实现完完全全的帧同步,不同电脑,受限于网络的延迟…...
hhdb数据库介绍(10-33)
管理 数据归档 归档记录查询 功能入口:“管理->数据归档->归档记录查询” 需要确保配置的归档用户对数据归档规则所在的逻辑库具备CREATE权限,以及对原数据表具有所有权限。 清理归档数据 (一)功能入口:“…...
UE4_材质节点_有关距离的_流体模拟
一、材质节点介绍: 特别注意:距离场需要独立显卡支持。 1、什么是距离场? 想象一下空间中只有两个实体, 一个球,一个圆柱. 空间由无数个点组成, 取其中任何一个点, 比如,它跟球面的最近距离是3, 跟圆柱面的最近距离是2, 那么这个点的值就…...
SpringBoot集成 SpringDoc (SpringFox 和 Swagger 的升级版)
阅读 SpringDoc 官网 - Migrating from SpringFox 只需要导入以下一个依赖即可: <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.7.0</version>…...
分布式推理框架 xDit
1. xDiT 简介 xDiT 是一个为大规模多 GPU 集群上的 Diffusion Transformers(DiTs)设计的可扩展推理引擎。它提供了一套高效的并行方法和 GPU 内核加速技术,以满足实时推理需求。 1.1 DiT 和 LLM DiT(Diffusion Transformers&am…...
《Vue零基础入门教程》第十七课:侦听器
往期内容 《Vue零基础入门教程》第九课:插值语法细节 《Vue零基础入门教程》第十课:属性绑定指令 《Vue零基础入门教程》第十一课:事件绑定指令 《Vue零基础入门教程》第十二课:双向绑定指令 《Vue零基础入门教程》第十三课&…...
【人工智能-基础】SVM中的核函数到底是什么
文章目录 支持向量机(SVM)中的核函数详解1. 什么是核函数?核函数的作用:2. 核技巧:从低维到高维的映射3. 常见的核函数类型3.1 线性核函数3.2 多项式核函数3.3 高斯径向基函数(RBF核)4. 总结支持向量机(SVM)中的核函数详解 支持向量机(SVM,Support Vector Machine)…...
RoBERTa- 稳健优化的 BERT 预训练模型详解
一、引言 自 BERT(Bidirectional Encoder Representations from Transformers)问世,预训练语言模型在自然语言处理(NLP)领域掀起革命浪潮,凭卓越表现大幅刷新诸多任务成绩。RoBERTa 承继 BERT 架构&#x…...
20.(开发工具篇github)Git上次超过100M单文件
1:安装lfs git lfs install 2: 撤销所有更改(包括未暂存的更改) git reset --hard 3:查找大于100M的文件 find ./ -size 100M 4:加入到 track git lfs track “./data/geo_tif_zzjg/2023年_种植结构影像.tif” git lfs track “./data/geo_tif_zz…...
Redis使用场景-缓存-缓存击穿
前言 之前在针对实习面试的博文中讲到Redis在实际开发中的生产问题,其中缓存穿透、击穿、雪崩在面试中问的最频繁,本文加了图解,希望帮助你更直观的了解缓存击穿😀 (放出之前写的针对实习面试的关于Redis生产问题的博…...
uniapp Electron打包生成桌面应用exe文件
1.uniapp Electron打包生成桌面应用exe文件 随着跨平台开发的需求日益增长,UniApp 成为了开发者们的首选之一。通过 UniApp,你可以使用 Vue.js 的语法结构和组件系统来构建原生应用、Web 应用甚至是桌面应用。本文将详细介绍如何使用 UniApp 将你的项目打包成 Windows 桌面端…...
【机器学习】Sigmoid函数在深层神经网络中存在梯度消失问题,如何设计一种改进的Sigmoid激活函数,既能保持其概率预测优势,又能避免梯度消失?
为了解决 Sigmoid 函数在深层神经网络中的梯度消失问题,可以设计一种改进的 Sigmoid 激活函数,使其同时具备以下特性: 减缓梯度消失问题:避免在输入值远离零时梯度趋于零的问题。保持概率预测能力:保留 Sigmoid 的单调…...
SpringBoot中实现EasyExcel实现动态表头导入(完整版)
前言 最近在写项目的时候有一个需求,就是实现动态表头的导入,那时候我自己也不知道动态表头导入是什么,查询了大量的网站和资料,终于了解了动态表头导入是什么。 一、准备工作 确保项目中引入了处理 Excel 文件的相关库ÿ…...
前端用到的一些框架
拖拽框架:Vue.Draggable Vue.Draggable是一款基于Sortable.js拖拽插件 官网:https://github.com/SortableJS/Vue.Draggable 分屏插件:fullPage.js fullPage.js 是一个基于 jQuery 的插件,它能够很方便、很轻松的制作出全屏网站…...
“量子跃迁与数据织网:深入探索K最近邻算法在高维空间中的优化路径、神经网络融合技术及未来机器学习生态系统的构建“
🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大二学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL࿰…...
10个Word自动化办公脚本
在日常工作和学习中,我们常常需要处理Word文档(.docx)。 Python提供了强大的库,如python-docx,使我们能够轻松地进行文档创建、编辑和格式化等操作。本文将分享10个使用Python编写的Word自动化脚本,帮助新…...
【青牛科技】D35摄氏温度传感器芯片,低功耗,静态工作电流小于60 μA
概述: D35是基于模拟电路的一种基本摄氏温度传感器,其作用是将感测的环境温度/物体温度精确的以电压的形式输出,且输出电压与摄氏温度成线性正比关系,转换公式为Vo0 10 mV / ℃*T(℃),0C时输出为…...
无分类编址的IPv4地址
/20含义:前20比特位为网络号,后面32-2012为主机号 路由聚合:找共同前缀 所有可分配地址的主机都能接收广播地址,...
LeetCode - #150 逆波兰表达式求值
文章目录 前言1. 描述2. 示例3. 答案关于我们 前言 我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。 LeetCode 算法到目前我们已经更新…...
如何避免数据丢失:服务器恢复与预防策略
在当今数字时代,数据对于个人和企业来说都至关重要。数据丢失可能会导致严重的财务损失、业务中断甚至法律责任。因此,采取措施防止数据丢失至关重要。本文将讨论服务器数据丢失的常见原因以及如何防止数据丢失的有效策略。 服务器数据丢失的常见原因 服…...
pytorch中model.eval的理解
在复现simsam的过程中,看到在线性评估部分的训练函数中设置了model.eval,不太理解,印象中一直觉得,model.eval会影响梯度的回传,这里来拨乱反正一下。 事实上,model.eval()主要影响 BatchNorm 和 Dropout 层的行为&am…...
【AI+教育】一些记录@2024.11.19-11.25
通向AGI之路:大型语言模型(LLM)技术精要 https://zhuanlan.zhihu.com/p/597586623 在Bert和GPT模型出现之前,NLP领域流行的技术是深度学习模型,而NLP领域的深度学习,主要依托于以下几项关键技术࿱…...
CSS变量用法及实践
目录 一、基本用法 1.1、定义变量 1.2、使用变量 1.3 、修改变量的值 二、命名规范 2.1、使用有意义的名称 2.2、使用命名空间 三、变量值类型 3.1、如果变量值是一个字符串,可以与其他字符串拼接,例如: 3.2、 如果变量值是数值&a…...
【Python网络爬虫笔记】8- (BeautifulSoup)抓取电影天堂2024年最新电影,并保存所有电影名称和链接
目录 一. BeautifulSoup的作用二. 核心方法介绍2.1 构造函数2.2 find()方法2.3 find_all()方法2.4 select()方法 三. 网络爬虫中使用BeautifulSoup四、案例爬取结果 一. BeautifulSoup的作用 解析HTML/XML文档:它可以将复杂的HTML或XML文本转换为易于操作的树形结构…...
STM32 ADC --- 知识点总结
STM32 ADC — 知识点总结 文章目录 STM32 ADC --- 知识点总结cubeMX中配置注解单次转换模式、连续转换模式、扫描模式单通道采样的情况单次转换模式:连续转换模式: 多通道采样的情况禁止扫描模式(单次转换模式或连续转换模式)单次…...
使用PHP脚本实现GitHub API搜索与数据库同步
在现代软件开发中,自动化数据收集和同步是提高效率的关键。今天,我将分享一个我最近开发的PHP脚本,它能够自动从GitHub API搜索特定关键词的仓库,并将这些数据同步到MySQL数据库中。这个过程不仅涉及到API调用和数据处理ÿ…...
使用docker-compese部署SFTPGo详解
官网:SFTP & FTP as a Managed Service (SaaS) and On-premise 一、SFTPGo简介 SFTPGo 是一款功能强大的文件传输服务器软件。它支持多种协议(SFTP、SCP、FTP/S、WebDAV、HTTP/S)和多个存储后端。 借助 SFTPGo,您可以利用本地…...
JavaScript根据数据生成柱形图
分析需求 // 定义一个数组来存储四个季度的数据 dataArray = []// 循环4次,获取用户输入的数据并存储到数组中 for i from 0 to 3// 获取用户输入的数据inputData = 获取用户输入的第(i + 1)季度的数据// 将数据存入数组dataArray[i] = inputData// 遍历数组,根据数据生成柱…...
Android笔记【12】脚手架Scaffold和导航Navigation
一、前言 学习课程时,对于自己不懂的点的记录。 对于cy老师第二节课总结。 二、内容 1、PPT介绍scaffold 2、开始代码实操 先新建一个screen包,写一个Homescreen函数,包括四个页面。 再新建一个compenent包,写一个displayText…...
VirtualBox注册已有虚拟机:未能打开位于虚拟电脑E_INVALIDARG (0X80070057)
错误如下 解决办法1 产生虚拟机的机器,与当前使用机器不兼容。建议在当前机器重新产生虚拟机。比如我家里电脑是WIN7,公司电脑是WIN11。 原来的虚拟机内容,找老机器导出。 解决办法2(存疑) 搜索到一个说法…...
开发中使用UML的流程_08 PIM-4:定义操作及方法
目录 1、序列图概述 2、序列图调用方式 3、创建消息与销毁消息 4、几项建议 1、序列图概述 在PIM-4中,系统分析员可以用序列图来表达,系统内部一群对象合力完成某一个系统用例时,执行期间的交互情形。之后,序列图可能通过设计…...
软件设计 —— 检测按键单击、多击、长按或组合动作
目 录 按键单一动作识别按键组合动作识别 按键单一动作识别 带有按键的作品设计时,按键动作检测是必不可少的,如何判断按键是单击、双击、三击和长按动作呢? 1、定时器定时一个10ms周期 2、把按键检测函数放到这个周期内执行,即…...
【GPT】主要影响代谢的因素
代谢的快慢受到多种因素的影响,包括遗传、生活习惯和健康状况等。以下是主要影响代谢的因素: 1. 年龄 影响:年龄增长会导致基础代谢率(BMR)逐渐降低,这是因为随着年龄增加,肌肉量减少ÿ…...
LLM Agents can Autonomously Hack Websites 论文阅读
paper:LLM Agents can Autonomously Hack Websites abstract:近年来,大型语言模型(llm)已经变得越来越有能力,现在可以与工具交互(例如,调用函数),读取文档,并递归地调用自己。因此,这些llm现在可以作为代理自主运行。随着这些代理能力的提高,最近的工作推测了LLM代…...
STM32标准库-FLASH
FLASH模仿EEPROM STM32本身没有自带EEPROM,但是自带了FLASH存储器。 STM32F103ZET6自带 1M字节的FLASH空间,和 128K64K的SRAM空间。 STM32F4 的 SPI 功能很强大,SPI 时钟最高可以到 37.5Mhz,支持 DMA,可以配置为 SPI协…...
【机器学习】机器学习的基本分类-监督学习-决策树-ID3 算法
ID3(Iterative Dichotomiser 3)是决策树的一种构造算法,由 Ross Quinlan 在 1986 年提出。它主要用于分类问题,通过信息增益选择特征来构建决策树。ID3 假设数据是离散型特征,且不支持连续型数据。 1. 核心思想 划分标…...
nginx配置http及https
nginx配置http及https 1.动静分离2.负载均衡3.配置https4.请求重定向5.常用参数配置介绍 现在日常工作中的项目大多数都是采用前后端分离,就用到了nginx进行反向代理、处理静态资源等;因此,记录整理了nginx一些常用的配置; 1.动静…...
威联通-001 手机相册备份
文章目录 前言1.Qfile Pro2.Qsync Pro总结 前言 威联通有两种数据备份手段:1.Qfile Pro和2.Qsync Pro,实践使用中存在一些区别,针对不同备份环境选择是不同。 1.Qfile Pro 用来备份制定目录内容的。 2.Qsync Pro 主要用来查看和操作文…...
柔性数组详解+代码展示
系列文章目录 🎈 🎈 我的CSDN主页:OTWOL的主页,欢迎!!!👋🏼👋🏼 🎉🎉我的C语言初阶合集:C语言初阶合集,希望能…...
【oracle数据库提示oracle initialization or shutdown in process】
问题如下截图: 解决方案: 1.进入sqlplus,下图中红圈即处理方式 备注:redo03.log是数据库路径“E:\app\Administrator\oradata\MKDB3D\”下最新的归档日志文件 2.alter database open resetlogs 3.netmanager测试登录是否成功&am…...
面试题-RocketMQ的基本架构、支持的消息模式、如何保证消息的可靠传输
相关问题 1、RocketMQ的基本架构是怎样的?请简述各组件的作用。 2、RocketMQ支持哪几种消息模式(如点对点、发布/订阅)?请简要说明它们的区别。 3、如何使用Java客户端实现一个简单的消息生产者和消费者? 4、RocketMQ…...
VUE脚手架练习
脚手架安装的问题: 1.安装node.js,配置环境变量,cmd输入node -v和npm -v可以看到版本号(如果显示不是命令,确认环境变量是否配置成功,记得配置环境变量之后重新打开cmd,再去验证) 2.在安装cnmp时…...
在Scala中栈的认识
package gjhs114import scala.collection.mutableobject fx {队列 // def main(args: Array[String]): Unit { // val q1 mutable.Queue(1) // q1.enqueue(2) // q1.enqueue(3) // q1.enqueue(4) // // println(q1.dequeue())//出队 1 // println(q1.dequ…...
小程序-基于java+SpringBoot+Vue的音乐播放器小程序设计与实现
项目运行 1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境:IDEA,Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境:Tomcat 7.x,8.x,9.x版本均可 4.硬件环境:…...
在21世纪的我用C语言探寻世界本质——字符函数和字符串函数(2)
人无完人,持之以恒,方能见真我!!! 共同进步!! 文章目录 一、strncpy函数的使用二、strncat函数的使用三、strncmp函数的使用四、strstr的使用和模拟实现五、strtok函数的使用六、strerror和perr…...
oracle to postgresql使用Oracle Golden Gate同步数据
参考 https://www.ktexperts.com/replication-to-gcp-postgresql-using-oracle-goldengate/ https://www.ktexperts.com/how-to-change-remote-trail-file-location-in-oracle-goldengate/...