Java中的线程池(如果想知道Java中有关线程池的知识,那么只看这一篇就足够了!)
前言:线程池是 Java 中用于高效管理并发任务的工具,通过复用线程、降低线程创建销毁的开销,提升系统性能与响应速度。它帮助开发者更好地控制线程生命周期和资源消耗,是高并发应用的重要组成部分。
✨✨✨这里是秋刀鱼不做梦的BLOG
✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客
在正式开始讲解之前,先让我们看一下本文大致的讲解内容:
目录
1.线程池的核心原理
(1)基本概念
(2)线程池的工作流程
(3)线程池的关键组件的实现方式
1. 核心线程数与最大线程数
2. 任务队列
3. 线程工厂
4. 拒绝策略
2.线程池的使用
(1)线程池的参数介绍
(2)使用 Executors 创建常见的线程池
【1】newFixedThreadPool(int nThreads) - 固定线程数线程池
【2】newCachedThreadPool() - 可缓存线程池
【3】newSingleThreadExecutor() - 单线程线程池
【4】newScheduledThreadPool(int corePoolSize) - 定时/周期性线程池
3.为什么要使用线程池
(1)降低资源的消耗
(2)提高速度
(3)提高线程的可管理性
1.线程池的核心原理
(1)基本概念
在正式开始学习Java中的线程池之前,先让我们了解一下什么是线程池,以下为线程池的基本概念:
在并发编程中,线程池是一个重要的工具,它允许我们复用已经创建的线程,从而减少线程创建和销毁的开销。线程池可以有效地管理并发任务,避免系统因为创建过多线程而产生的性能瓶颈。
在了解完了线程池的基本概念之后,让我们看一下线程池的组成部分,线程池的基本结构通常由以下几个部分组成:
核心线程数 (
corePoolSize
):线程池中常驻的线程数。即使没有任务执行,线程池也会保持这些线程在空闲状态。最大线程数 (
maximumPoolSize
):线程池中允许的最大线程数。如果任务数超过线程池的核心线程数并且任务队列已满,线程池将会创建更多的线程,直到达到最大线程数。任务队列:用于存放提交到线程池的任务。不同类型的任务队列会影响线程池的性能。例如,
LinkedBlockingQueue
是一个无界队列,可以存放大量任务,而ArrayBlockingQueue
是一个有界队列,适合在任务数有上限的场景中使用。线程工厂 (
ThreadFactory
):用于创建新线程。你可以自定义线程工厂,以便为线程池中的线程命名或设置优先级等属性。拒绝策略 (
RejectedExecutionHandler
):当线程池无法接受新的任务时,可以采用不同的拒绝策略,比如丢弃任务或抛出异常。
读者在读完上述对线程池的组成部分的描述之后,可能还是不能理解线程池中的这些构成部分,不过没关系,随着我们对线程池的进一步讲解之后,读者就可以更好的理解这些部分了!
(2)线程池的工作流程
在了解完了线程池的基本概念之后,在让我们进一步了解一下Java中的线程池是如何工作的,其工作原理又是什么
线程池的基本工作流程可以分为以下几个步骤:
程可以分为以下几个步骤:
任务提交: 当一个任务被提交给线程池时,线程池首先会尝试将任务放入任务队列。如果任务队列有空间,任务就会排队等待执行。如果任务队列满了,且线程池中的线程数未达到最大线程数,则线程池会创建新的线程来处理任务。如果线程池中的线程数已经达到最大值,且任务队列也满了,线程池会根据配置的拒绝策略来处理任务。
任务执行: 线程池中的线程会从任务队列中获取任务并执行。执行完成后,线程并不会被销毁,而是返回到线程池中,准备接收下一个任务。
线程回收: 如果线程池中的线程长时间处于空闲状态,且空闲时间超过了设定的阈值,线程池会回收这些线程,以节省系统资源。回收的线程数不会低于核心线程数,只有当线程数大于核心线程数时,线程池才会销毁空闲线程。
拒绝策略触发: 如果线程池的任务队列已满,并且线程池中的线程数已经达到了最大线程数,再提交的任务就会被拒绝。这时,线程池会根据配置的拒绝策略来处理任务,如抛出异常、丢弃任务、丢弃队列中最老的任务,或者由提交任务的线程自己执行任务。
通过上述的讲解,我们就大致的了解了Java中的线程池是如何工作的了,这对于我们接下来的学习是至关重要的。
(3)线程池的关键组件的实现方式
在上文中我们已经了解了Java中的线程池的基本结构了,这里我们进一步讲解一下线程池的关键组件的实现方式。
线程池的组件包括核心线程数、最大线程数、任务队列、线程工厂和拒绝策略,这些组件的配置决定了线程池的行为与性能,下面是每个组件的详细介绍。
1. 核心线程数与最大线程数
核心线程数:线程池在没有任务时保持的最小线程数,避免了线程的频繁创建和销毁。通过合理设置
corePoolSize
,可以确保在任务负载较轻时,线程池仍然保持一定数量的线程,以便及时响应新的任务。最大线程数:线程池允许的最大线程数。当任务量剧增且任务队列已满时,线程池会根据
maximumPoolSize
来创建新的线程,但不会超过该最大值。
2. 任务队列
线程池的任务队列有不同的实现方式:
LinkedBlockingQueue
:一个无界队列,适用于任务量较大的场景,能容纳大量待处理的任务。若任务队列中有空位,线程池就会继续添加任务,不会立即创建新的线程。
ArrayBlockingQueue
:一个有界队列,适用于任务量较小且固定的场景。当任务队列已满时,线程池会尝试创建新的线程,直到达到最大线程数。
SynchronousQueue
:一个零容量队列,适合任务较为紧凑并且执行迅速的场景。每当一个任务到来时,必须有线程立即接收并执行这个任务。
3. 线程工厂
线程工厂用于创建线程。通过自定义线程工厂,开发者可以为线程指定特定的名称、优先级,或者让线程成为守护线程等。
4. 拒绝策略
线程池中的拒绝策略用于处理任务过载时的情况。常见的拒绝策略包括:
AbortPolicy
:默认策略,抛出RejectedExecutionException
异常。
DiscardPolicy
:丢弃当前任务。
DiscardOldestPolicy
:丢弃任务队列中最旧的任务。
CallerRunsPolicy
:让提交任务的线程来执行该任务。
至此,我们就大致的对Java中的线程池有了初步的理解了!!!
2.线程池的使用
在了解完了Java中的线程池的基本概念以及原理之后,现在让我们学习一下如何去使用Java中的线程池吧,不过首先我们需要先了解一下Java中的线程池的参数。
(1)线程池的参数介绍
Java 中使用 ThreadPoolExecutor
来创建自定义线程池。通过构造方法,可以传入多个参数来配置线程池,具体参数如下:
public ThreadPoolExecutor(int corePoolSize, // 核心线程数int maximumPoolSize, // 最大线程数long keepAliveTime, // 线程空闲存活时间TimeUnit unit, // 线程空闲存活时间的单位BlockingQueue<Runnable> workQueue, // 任务队列ThreadFactory threadFactory, // 线程工厂RejectedExecutionHandler handler // 拒绝策略
);
参数解释:
corePoolSize(核心池大小)
- 定义:核心池大小是线程池中始终保持活动的线程数,即使线程池中的任务数较少,核心线程也会一直存在,除非设置了
allowCoreThreadTimeOut
为true
。 - 作用:决定了线程池中最小的线程数量。这些线程会一直存活,直到线程池被关闭。
- 调优建议:对于 CPU 密集型任务,可以设置为与 CPU 核心数相等;对于 IO 密集型任务,可以适当增大。
int corePoolSize = 5; // 核心线程数
maximumPoolSize(最大池大小)
- 定义:最大池大小是线程池中能够创建的最大线程数。当任务队列满了,且线程池中的线程数小于
maximumPoolSize
时,线程池会创建新线程来处理任务。 - 作用:限制线程池中最大的并发线程数。如果任务量非常大,且有大量任务需要处理,
maximumPoolSize
设得较大可以避免任务的阻塞。 - 调优建议:如果系统的硬件资源充足,且任务的数量和处理时间不确定,可以适当增加
maximumPoolSize
。
int maximumPoolSize = 10; // 最大线程数
keepAliveTime(线程存活时间)
- 定义:当线程池中的线程数超过核心线程数时,空闲线程的最大存活时间。超出这个时间,线程会被终止并从池中移除。
- 作用:如果线程池中的线程多于核心线程数,但线程在一定时间内未被使用,那么这些线程会被回收。
- 调优建议:对于任务量变化大的应用,可以适当调整
keepAliveTime
,以节省资源。对于高并发任务,可以适当增加此值。
long keepAliveTime = 60L; // 线程存活时间,单位为秒
unit(时间单位)
- 定义:
keepAliveTime
参数的时间单位,通常是TimeUnit
类提供的常量,如TimeUnit.SECONDS
、TimeUnit.MILLISECONDS
、TimeUnit.MINUTES
等。 - 作用:决定
keepAliveTime
使用的单位,方便开发者在设置时选择不同的时间粒度。 - 调优建议:如果线程池中的线程空闲时间较短,可以选择秒作为时间单位;如果线程空闲时间较长,选择分钟等较大时间单位。
TimeUnit unit = TimeUnit.SECONDS; // 设置时间单位为秒
workQueue(任务队列)
- 定义:线程池中的任务队列,用于存储等待执行的任务。常见的任务队列有
LinkedBlockingQueue
、ArrayBlockingQueue
、SynchronousQueue
等。 - 作用:当线程池中的线程数量达到
corePoolSize
时,新提交的任务会被放入任务队列等待执行。任务队列的选择直接影响线程池的性能。 - 调优建议:对于任务数量不确定的情况,可以选择无界队列(如
LinkedBlockingQueue
);如果需要限制队列的大小,则可以使用有界队列(如ArrayBlockingQueue
)。
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100); // 设置任务队列容量为100
handler(拒绝策略)
- 定义:当线程池中的线程数达到
maximumPoolSize
,且任务队列已满时,新的任务提交就会被拒绝。此时可以通过RejectedExecutionHandler
处理任务的拒绝。 - 作用:拒绝策略决定了当任务无法被线程池处理时的处理方式。常见的拒绝策略有:
AbortPolicy
:直接抛出RejectedExecutionException
,默认策略。CallerRunsPolicy
:由调用线程处理该任务,避免任务丢失。DiscardPolicy
:直接丢弃任务。DiscardOldestPolicy
:丢弃队列中最旧的任务。
- 调优建议:如果任务丢失不可接受,推荐使用
CallerRunsPolicy
。如果可以容忍任务丢失,则可以选择DiscardPolicy
。
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); // 使用调用者运行策略
threadFactory(线程工厂)
- 定义:线程池使用线程工厂来创建新的线程。可以自定义线程工厂,以定制线程的创建过程(如设置线程的名称、优先级、是否为守护线程等)。
- 作用:
threadFactory
允许开发者控制线程的创建过程,特别是在需要对线程进行一些特殊配置(如设置线程名称、线程优先级、守护线程等)时非常有用。 - 调优建议:通常情况下,使用默认的线程工厂就足够了。如果需要自定义线程行为,可以实现
ThreadFactory
接口。
ThreadFactory threadFactory = new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setName("CustomThread-" + thread.getId());return thread;}
};
通过上述对线程池中的参数的讲解,我们就大致的了解了Java中线程池该如果创建了,那么现在让我们使用一个案例将上述所讲的串联起来,以下为一个自定义线程池:
import java.util.concurrent.*;public class CustomThreadPool {public static void main(String[] args) {int corePoolSize = 5; // 核心线程数int maximumPoolSize = 10; // 最大线程数long keepAliveTime = 60L; // 线程存活时间TimeUnit unit = TimeUnit.SECONDS; // 时间单位BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100); // 任务队列RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); // 拒绝策略ThreadFactory threadFactory = new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setName("CustomThread-" + thread.getId());return thread;}};ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);// 提交任务for (int i = 0; i < 10; i++) {int taskId = i;executor.submit(() -> {System.out.println("执行任务 " + taskId + ",线程: " + Thread.currentThread().getName());});}executor.shutdown();}
}
在这个示例中,我们使用了自定义的线程池参数来创建一个 ThreadPoolExecutor
,并提交了任务来执行。通过配置这些参数,我们能够更好地控制线程池的行为,确保系统在高并发条件下高效运行。
(2)使用 Executors
创建常见的线程池
在Java中除了ThreadPoolExecutor之外,Executors
工厂类也为我们提供了几种常用的线程池创建方法,下面是几种常见线程池的创建和使用方法
【1】newFixedThreadPool(int nThreads)
- 固定线程数线程池
这种线程池创建一个固定大小的线程池,线程池中的线程数在创建后保持不变。当所有线程都处于工作状态时,新任务将进入等待队列中,直到有线程空闲出来。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class FixedThreadPoolExample {public static void main(String[] args) {// 创建一个固定线程数量为 3 的线程池ExecutorService executor = Executors.newFixedThreadPool(3);for (int i = 0; i < 5; i++) {executor.execute(() -> {System.out.println(Thread.currentThread().getName() + " 正在执行任务");});}// 关闭线程池executor.shutdown();}
}
这种方式创建线程池的特点:
- 适用于执行长期任务,性能稳定。
- 线程池中的线程数固定,不会变化。
- 如果所有线程都在工作,新的任务会被放入等待队列中,等待有空闲线程时执行。
【2】newCachedThreadPool()
- 可缓存线程池
这种线程池会根据任务需要创建新线程,并复用先前构建的线程。池中的线程如果在 60 秒内都没有被使用,则会被终止并从池中移除。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CachedThreadPoolExample {public static void main(String[] args) {// 创建一个可缓存的线程池ExecutorService executor = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {executor.execute(() -> {System.out.println(Thread.currentThread().getName() + " 正在执行任务");});}// 关闭线程池executor.shutdown();}
}
这种方式创建线程池的特点:
- 适用于执行大量短期任务。
- 当线程空闲 60 秒后自动回收,避免资源浪费。
- 线程池大小不固定,按需动态分配。
【3】newSingleThreadExecutor()
- 单线程线程池
这种线程池始终使用唯一的工作线程来执行任务,所有任务按提交顺序执行。如果该线程异常终止,一个新线程会取而代之,继续执行后续任务。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class SingleThreadExecutorExample {public static void main(String[] args) {// 创建一个单线程化的线程池ExecutorService executor = Executors.newSingleThreadExecutor();for (int i = 0; i < 5; i++) {executor.execute(() -> {System.out.println(Thread.currentThread().getName() + " 正在执行任务");});}// 关闭线程池executor.shutdown();}
}
这种方式创建线程池的特点:
- 适用于需要保证任务顺序执行的场景。
- 只有一个线程工作,所有任务会按顺序执行。
- 可确保任务按提交顺序执行。
【4】newScheduledThreadPool(int corePoolSize)
- 定时/周期性线程池
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledThreadPoolExample {public static void main(String[] args) {// 创建一个支持定时及周期性任务的线程池ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);// 延迟 2 秒后执行任务executor.schedule(() -> {System.out.println("延迟 2 秒后执行任务");}, 2, TimeUnit.SECONDS);// 延迟 1 秒后开始执行任务,之后每 3 秒执行一次executor.scheduleAtFixedRate(() -> {System.out.println("每 3 秒执行一次任务");}, 1, 3, TimeUnit.SECONDS);// 关闭线程池executor.shutdown();}
}
这种方式创建线程池的特点:
- 适用于需要周期性执行任务的场景。
- 可以指定延迟执行,也可以按照固定的时间间隔循环执行。
以上就是使用Java中使用Executors
工厂类来创建线程池的方式了!至此我们就了解了Java中该如何创建并使用线程池了。
3.为什么要使用线程池
在了解完了如何在Java中使用线程池之后,可能读者就会发问了,我们为什么要使用线程池呢?线程池有什么优点呢?那么我们这就解释一下为什么使用线程池。
(1)降低资源的消耗
线程池通过复用线程池中的线程,避免了频繁的线程创建和销毁,从而降低了资源消耗。每次创建线程的成本较高,尤其是在并发量大的场景中,频繁地创建和销毁线程会导致系统性能下降。线程池通过维持一定数量的线程,复用这些线程处理任务,减少了频繁创建线程的开销。
示例:
public class ThreadCreationTest {public static void main(String[] args) {long startTime = System.nanoTime();for (int i = 0; i < 1000; i++) {new Thread(() -> {// 模拟一些简单的计算任务for (int j = 0; j < 1000; j++) {Math.sqrt(j);}}).start();}long endTime = System.nanoTime();System.out.println("线程创建和销毁的时间: " + (endTime - startTime) + " 纳秒");// 使用线程池ExecutorService executorService = Executors.newFixedThreadPool(10);startTime = System.nanoTime();for (int i = 0; i < 1000; i++) {executorService.submit(() -> {// 模拟一些简单的计算任务for (int j = 0; j < 1000; j++) {Math.sqrt(j);}});}executorService.shutdown();endTime = System.nanoTime();System.out.println("线程池的时间: " + (endTime - startTime) + " 纳秒");}
}
上面的代码模拟了两种方式:直接创建线程和使用线程池处理任务。通过对比这两者的时间消耗,我们就可以看到线程池显著减少了线程创建和销毁的开销。
(2)提高速度
线程池通过预先创建一定数量的线程,可以在任务到来时迅速响应,当任务提交到线程池时,如果有空闲线程,线程池可以立即开始执行任务,避免了任务排队等待线程创建的时间,确保任务尽可能快地被处理。
示例:
public class RequestHandler {private static final int THREAD_POOL_SIZE = 10;private static final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);public static void handleRequest(int requestId) {executorService.submit(() -> {try {System.out.println("处理请求 " + requestId + " 的线程:" + Thread.currentThread().getName());Thread.sleep(200); // 模拟处理请求的时间} catch (InterruptedException e) {e.printStackTrace();}});}public static void main(String[] args) {for (int i = 0; i < 50; i++) {handleRequest(i);}executorService.shutdown();}
}
在这个例子中,我们模拟了 50 个请求的并发处理。线程池使用了 10 个线程来并发处理请求,并且任务的执行时间(Thread.sleep(200)
)模拟了请求的处理过程。通过线程池,多个请求可以同时被多个线程处理,而不需要等待线程的创建。
(3)提高线程的可管理性
线程池不仅能有效地复用线程,还提供了线程的生命周期管理。通过合理设置线程池的参数,开发者可以控制线程的创建、销毁以及空闲时的回收方式,从而确保系统在负载较重时仍能稳定运行。
示例:
import java.util.concurrent.*;public class DynamicThreadPoolTest {private static final int CORE_POOL_SIZE = 5;private static final int MAX_POOL_SIZE = 10;private static final int QUEUE_CAPACITY = 20;private static ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(QUEUE_CAPACITY));public static void handleTask(int taskId) {executor.submit(() -> {try {System.out.println("任务 " + taskId + " 正在执行,线程: " + Thread.currentThread().getName());Thread.sleep(200); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}});}public static void main(String[] args) {for (int i = 0; i < 100; i++) {handleTask(i);}// 动态调整线程池的大小executor.setCorePoolSize(7);executor.setMaximumPoolSize(15);System.out.println("线程池已调整为新的配置");// 关闭线程池executor.shutdown();}
}
在上述代码中,线程池最初使用了 5 个核心线程和 10 个最大线程,但在任务提交过程中,我们根据负载情况动态调整了线程池的大小(通过调用 setCorePoolSize
和 setMaximumPoolSize
)。这种动态调整可以有效应对负载变化,从而优化线程池的性能。
这样我们就理解了,为什么要使用线程池了!!!
以上就是本篇文章的全部内容了~~~
相关文章:
Java中的线程池(如果想知道Java中有关线程池的知识,那么只看这一篇就足够了!)
前言:线程池是 Java 中用于高效管理并发任务的工具,通过复用线程、降低线程创建销毁的开销,提升系统性能与响应速度。它帮助开发者更好地控制线程生命周期和资源消耗,是高并发应用的重要组成部分。 ✨✨✨这里是秋刀鱼不做梦的BLO…...
Java 获取本机 IP 地址的方法
文章目录 一、使用 InetAddress.getLocalHost二、遍历网络接口获取 在 Java 编程中,若有本机的 IP 地址的需求,小编来展示一下方法: 一、使用 InetAddress.getLocalHost 一是最基本的获取本机 IP 地址的方式。 示例代码: impo…...
开源动态表单form-create-designer 扩展个性化配置的最佳实践教程
在开源低代码表单设计器 form-create-designer 的右侧配置面板里,field 映射规则为开发者提供了强大的工具去自定义和增强组件及表单配置的显示方式。通过这些规则,你可以简单而高效地调整配置项的展示,提升用户体验。 源码地址: Github | G…...
c语言数据结构与算法--简单实现线性表(顺序表+链表)的插入与删除
老规矩,点赞评论收藏关注!!! 目录 线性表 其特点是: 算法实现: 运行结果展示 链表 插入元素: 删除元素: 算法实现 运行结果 线性表是由n个数据元素组成的有限序列ÿ…...
5、AI测试辅助-生成测试用例思维导图
AI测试辅助-生成测试用例思维导图 创建测试用例两种方式1、Plantuml思维导图版本 (不推荐)2、Markdown思维导图版本(推荐) 创建测试用例两种方式 完整的测试用例通常需要包含以下的元素: 1、测试模块 2、测试标题 3、前置条件 4、…...
c ++零基础可视化——vector
c 零基础可视化——vector 初始化 vector<int> v0(5); // 0 0 0 0 0 vector<int> v1(5, 1); // 1 1 1 1 1 vector<int> v2{1, 2, 3} // 1 2 3 vector<int> v3(v1); // 1 1 1 1 1 vector<vector<int>> v4(2, vect…...
网络安全应急响应及其发展方向
计算机信息系统和网络已经成为社会重要的基础设施,而网络安全始终是笼罩在Internet天空的一片乌云,对社会的威胁也与日俱增。自1988年莫里斯蠕虫事件以来,网络安全事件逐年上升,所造成的损失越来越大。自从CERT/CC成立以来&#x…...
Linux应用编程(C语言编译过程)
目录 1. 举例 2.预处理 2.1 预处理命令 2.2 .i文件内容解读 3.编译 4.汇编 5.链接 5.1 链接方式 5.1.1 静态链接 5.1.2 动态链接 5.1.3 混合链接 1. 举例 Linux的C语言开发,一般选择GCC工具链进行编译,通过下面的例子来演示GCC如何使用&#…...
003 STM32基础、架构以及资料介绍——常识
注: 本笔记参考学习B站官方视频教程,免费公开交流,切莫商用。内容可能有误,具体以官方为准,也欢迎大家指出问题所在。 01什么是STM32(宏观) STM32属于一个微控制器,自带了各种常用通…...
输入三个整数x,y,z,请把这三个数由小到大输出。-多语言实现
目录 C 语言实现 Python 实现 Java 实现 Js 实现 题目:输入三个整数x,y,z,请把这三个数由小到大输出。 程序分析:我们想办法把最小的数放到x上,先将x与y进行比较,如果x>y则将x与y的值进行交换,然后…...
Java爬虫:获取商品详情的实践之旅
在当今这个信息爆炸的时代,数据的价值日益凸显。对于电商行业来说,商品详情的获取尤为重要,它不仅关系到产品的销售,还直接影响到用户体验。传统的人工获取方式耗时耗力,而自动化的爬虫技术则提供了一种高效解决方案。…...
vue 富文本图片如何拖拽
在Vue项目中实现富文本编辑器(如vue-quill-editor)的图片拖拽功能,需要结合Quill.js及其相关插件进行配置 安装必要的依赖包: 你需要安装vue-quill-editor作为富文本编辑器的基础组件。为了支持图片拖拽功能,你还需要…...
C#里怎么样实现自己的类满足for-each循环访问?
C#里怎么样实现自己的类满足for-each循环访问? 由于foreach语句使用起来比较方便,并且又简单,不像for循环那样需要写三个语句, 并且还有可能判断不对,导致少访问的情况。 在使用库里提供的容器时,都会有实现foreach的方式。 如果自己的类也实现这种方式,使用起来就比较…...
【Elasticsearch入门到落地】2、正向索引和倒排索引
接上篇《1、初识Elasticsearch》 上一篇我们学习了什么是Elasticsearch,以及Elastic stack(ELK)技术栈介绍。本篇我们来什么是正向索引和倒排索引,这是了解Elasticsearch底层架构的核心。 上一篇我们学习到,Elasticsearch的底层是由Lucene实…...
ODBC连接PostgreSQL数据库后,网卡DOWN后,客户端进程阻塞问题解决方法
问题现象:数据库客户端进程数据库连接成功后,再把跟数据库交互的网卡down掉,客户端进程就会阻塞,无法进行其他处理。该问题跟TCP keepalive机制有关。 可以在odbc.ini文件中增加相应的属性来解决,在odbc.ini 增加如下…...
Java Springboot河北任丘非遗传承宣传平台
一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据…...
SpringBoot整合MQTT利用EMQX完成消息的发布与接收+Python模拟硬件测试通信
教程说明 本教程主要内容为使用SpringBoot整合MQTT利用EMQX代理服务完成MQTT的消息发送与接收,然后用Python模拟硬件与SpringBoot应用进行了MQTT消息的通信,教程详细,并在最后讲解了开发中的注意事项,本教程适用于物联网领域、Ja…...
【数据结构】【线性表】栈的基本概念(附c语言源码)
栈的基本概念 讲基本概念还是回到数据结构的三要素:逻辑结构,物理结构和数据运算。 从逻辑结构来讲,栈的各个数据元素之间是通过是一对一的线性连接,因此栈也是属于线性表的一种从物理结构来说,栈可以是顺序存储和顺…...
【jvm】为什么java是半编译半解释型语言
目录 1. 编译过程2. 解释过程3. 即时编译(JIT)过程4. 半编译半解释型语言的特点 1. 编译过程 1.Java源代码首先会被编译成字节码(Bytecode),这是一种与具体平台无关的中间代码。2.这一编译过程由Java编译器࿰…...
人工智能之机器学习5-回归算法2【培训机构学习笔记】
培训班ppt内容: 个人精进总结: 可解释方差 定义 可解释方差的回归评分函数是一种用于评估回归模型性能的指标,以下从其定义、计算公式、取值范围及意义、应用场景等方面进行详细介绍: 可解释方差(Explained Varian…...
C#动态类型详解:应用场景与注意事项
C#中的动态类型(dynamic)是C# 4.0引入的一个关键字,它允许在编译时绕过类型检查,将类型确定推迟到运行时。这意味着,当使用dynamic类型时,编译器不会对调用的方法或访问的属性进行类型检查,而是…...
C语言笔记(自定义类型:结构体、枚举、联合体 )
前言 本文对自定义类型的结构体创建、使用、结构体的存储方式和对齐方式,枚举的定义、使用方式以及联合体的定义、使用和存储方式展开叙述,如有错误,请各位指正。 目录 前言 1 结构体 1.1 结构体的声明 1.2 结构体的自引用 1.3 结构体变…...
【Golang】——Gin 框架与数据库集成详解
文章目录 1. 引言2. 初始化项目2.1 创建 Gin 项目2.2 安装依赖 3. 数据库驱动安装与配置3.1 配置数据库3.2 连接数据库3.3 在主函数中初始化数据库 4. 定义数据模型4.1 创建用户模型4.2 自动迁移 5. 使用 GORM 进行 CRUD 操作5.1 创建用户5.2 获取用户列表5.3 更新用户信息5.4 …...
nature communications论文 解读
题目《Transfer learning with graph neural networks for improved molecular property prediction in the multi-fidelity setting》 这篇文章主要讨论了如何在多保真数据环境(multi-fidelity setting)下,利用图神经网络(GNNs&…...
selinux及防火墙
selinux说明 SELinux 是 Security-Enhanced Linux 的缩写,意思是安全强化的 linux 。 SELinux 主要由美国国家安全局( NSA )开发,当初开发的目的是为了避免资源的误用。 httpd进程标签(/usr/share/nginx/html &#…...
实验二 系统响应及系统稳定性
实验目的 (1)学会运用Matlab 求解离散时间系统的零状态响应; (2)学会运用Matlab 求解离散时间系统的单位取样响应; (3)学会运用Matlab 求解离散时间系统的卷积和。 实验原理及实…...
【人工智能】深度学习入门:用TensorFlow实现多层感知器(MLP)模型
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 多层感知器(MLP)是一种基础的神经网络结构,广泛应用于分类和回归任务。作为深度学习的重要组成部分,理解并实现MLP是学习更复杂神经网络模型的基础。本文将介绍多层感知器的核心概念、数学原理,并使用…...
设计模式之建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,旨在将一个复杂对象的构建过程与其表示分离。它允许通过一步步地构造对象,而不需要暴露对象的内部细节和构建过程。通常,这个模式适用于创建对象时需要多个步骤&…...
谈一下开源生态对 AI人工智能大模型的促进作用
谈一下开源生态对 AI人工智能大模型的促进作用 作者:开源呼叫中心系统 FreeIPCC,Github地址:https://github.com/lihaiya/freeipcc 开源生态对大模型的促进作用是一个多维度且深远的话题,它不仅加速了技术创新的速度,…...
数据结构——树与二叉树
树 介绍 n个节点的有效集,它可为空树或非空树; 树是一种递归的结构。 对于非空树: 有且仅有一个称为根的节点。 除根节点以外其余节点可分为m个互不相交的有限集,且这些有限集本身也是一棵树,称为根的子树。 分等…...
神经网络(系统性学习三):多层感知机(MLP)
相关文章: 神经网络中常用的激活函数 神经网络(系统性学习一):入门篇 神经网络(系统性学习二):单层神经网络(感知机) 多层感知机(MLP) 多层感…...
Python学习——字符串操作方法
mystr “hello word goodbye” str “bye” Find函数:检测一个字符串中是否包含另一个字符串,找到了返回索引值,找不到了返回-1 print(mystr.find(str,0,len(mystr))) print(mystr.find(str,0,13)) index函数:检测一个字符串是否包含另一…...
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
css使用弹性盒,让每个子元素平均等分父元素的4/1大小 原本: ul {padding: 0;width: 100%;background-color: rgb(74, 80, 62);display: flex;justify-content: space-between;flex-wrap: wrap;li {/* 每个占4/1 */overflow: hidden;background-color: r…...
C语言练习.if.else语句.strstr
今天在做题之前,先介绍一下,新学到的库函数strstr 想要使用它,要先给它一个头文件<string.h> char *strstr(const char*str1,const char*str2); 首先:1.strstr的返回值是char,字符类型的。 2.两个实参ÿ…...
STM32编程小工具FlyMcu和STLINK Utility 《通俗易懂》破解
FlyMcu FlyMcu 模拟仿真软件是一款用于 STM32 芯片 ISP 串口烧录程序的专用工具,免费,且较为非常容易下手,好用便捷。 注意:STM32 芯片的 ISP 下载,只能使用串口1(USART1),对应的串口…...
拉格朗日乘子(Lagrange Multiplier)是数学分析中用于解决带有约束条件的优化问题的一种重要方法,特别是SVM
拉格朗日乘子(Lagrange Multiplier)是数学分析中用于解决带有约束条件的优化问题的一种重要方法,也称为拉格朗日乘数法。 例如之前博文写的2月7日 SVM&线性回归&逻辑回归在支持向量机(SVM)中,为了…...
计算机网络——数据链路层
计算机广域网如果采用多点连接的方式: 因为广域网的链路中带宽大,延迟大,很有可能发送碰撞导致数据错误 而且布局困难...
HTMLCSS:3D金字塔加载动画
效果演示 这段代码通过CSS3的3D变换和动画功能,创建了一个旋转的金字塔加载动画,每个侧面都有不同的颜色渐变,底部还有一个模糊的阴影效果,增加了视觉的立体感。 HTML <div class"pyramid-loader"><div cl…...
Charles抓包工具-笔记
摘要 概念: Charles是一款基于 HTTP 协议的代理服务器,通过成为电脑或者浏览器的代理,然后截取请求和请求结果来达到分析抓包的目的。 功能: Charles 是一个功能全面的抓包工具,适用于各种网络调试和优化场景。 它…...
【Github】如何使用Git将本地项目上传到Github
【Github】如何使用Git将本地项目上传到Github 写在最前面1. 注册Github账号2. 安装Git工具配置用户名和邮箱仅为当前项目配置(可选) 3. 创建Github仓库4. 获取仓库地址5. 本地操作(1)进入项目文件夹(2)克隆…...
Express编写中间件
中间件 注意上面的调用next()。调用此函数会调用应用程序中的下一个中间件函数。该next()函数不是Node.js或Express API的一部分,而是传递给中间件函数的第三个参数。该next()函数可以命名为任何东西,但按照惯例,它总是被命名为“next”。为避…...
【Spring Boot】# 使用@Scheduled注解无法执行定时任务
1. 前言 在 Spring Boot中,使用Scheduled注解来定义定时任务时,定时任务不执行;或未在规定时间执行。 import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;Component public c…...
Java基于SpringBoot+Vue的藏区特产销售平台
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...
_computed _destinations() 为什么模板不写()
_computed _destinations() 为什么模板不写() 在 Vue 模板中使用计算属性时,不需要在属性名称后面加上括号 ()。计算属性本质上是一个 getter 函数,但 Vue 会将其转换为一个响应式的属性。Vue 会自动调用这些计算属性并将其结果作为属性值使用。 计算属…...
接口上传视频和oss直传视频到阿里云组件
接口视频上传 <template><div class"component-upload-video"><el-uploadclass"avatar-uploader":action"uploadImgUrl":on-progress"uploadVideoProcess":on-success"handleUploadSuccess":limit"lim…...
SimbaSchema在数据云平台中的可观测性实践与应用
建立可观测性体系是很多数据部门的理想。从Logs、Metrics、Traces和Meta抓取系统状态,到建立数据模型和指标体系,全流程均通过数据来支持运维。建立可观测性体系具备其行业标准,即所有数据平台类基础设施理论上都应提供Infomation Schema可观…...
VB、VBS、VBA的区别及作用
VB、VBS 和 VBA 是三种与微软 Visual Basic 相关的编程语言或环境,它们在功能和用途上有所不同: # Visual Basic (VB) Visual Basic 是一种面向对象的编程语言,最初由微软公司开发。它是一种高级编程语言,旨在简化开发过程&…...
elasticsearch的文档管理
2 json数据入门 json数据类型 基础数据类型 字符串,只要用双引号包裹的都是字符串类型。“嘻嘻”,“文勇”,“2024” 数字型,2024,3.1415926 布尔型,true 和 false 空值,null 高级数据类…...
Java基础-I/O流
(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 字节流 定义 说明 InputStream与OutputStream示意图 说明 InputStream的常用方法 说明 OutputStrea…...
【CVE-2024-9413】SCP-Firmware漏洞:安全通告
安全之安全(security)博客目录导读 目录 一、概述 二、修订历史 三、CVE根因分析 四、问题修复解决 一、概述 在SCP固件中发现了一个漏洞,如果利用该漏洞,可能会允许应用处理器(AP)在系统控制处理器(SCP)固件中导致缓冲区溢出。 CVE IDCVE-2024-9413受影响的产品SC…...