当前位置: 首页 > news >正文

Java多线程与线程池技术详解(四)

接受失败:“失败是什么?没有什么,只是更走近成功一步;成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。”这句话很好地诠释了如何看待失败的问题,即每一次跌倒都是通往胜利道路上不可或缺的一部分。

创造机会:“不要等待机会,而要创造机会。”这句话鼓励人们主动出击,不要总是期待外界给予完美的条件或时机,而是要勇于尝试,创造属于自己的机遇。


目录

上一篇博客课后习题

编写一个简单的生产者-消费者模式程序

实现一个多阶段流水线作业

设计一个支持动态调整线程数量的线程池框架

分析并修复一段包含潜在死锁风险的代码片段

探索LockSupport.park()和unpark()的用法

第4章 线程池入门

4.1 ThreadPoolExecutor

4.1.1 创建线程池

4.1.2 关闭线程池

4.2 Executor接口

4.3 ExecutorService 接口

4.3.1 Callable返回任务执行结果

4.3.2 .shutdown与shutdownNow

4.4 Executors 工具箱

4.5 线程工厂与线程组

4.6 线程池异常处理

4.7 本章习题


上一篇博客课后习题

编写一个简单的生产者-消费者模式程序

根据提供的资料,我们可以创建一个使用java.util.concurrent包中工具来实现生产者-消费者模式的例子。这里我们将采用BlockingQueue接口的一个具体实现如ArrayBlockingQueue作为缓冲区。该队列允许我们利用其内置的方法put()take()来进行阻塞操作,确保当队列满时生产者会被阻塞,而当队列为空时消费者也会被阻塞等待元素出现。下面是一个简化的代码示例:

import java.util.concurrent.*;public class ProducerConsumerExample {private static final int BUFFER_SIZE = 10;private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(BUFFER_SIZE);public static void main(String[] args) throws InterruptedException {Thread producer = new Thread(() -> {try {for (int i = 0; ; i++) {System.out.println("Producing item: " + i);queue.put(i); // 如果队列已满,则此方法会自动阻塞直到有空间可用System.out.println("Buffer size after production: " + queue.size());Thread.sleep(100); // 模拟生产时间}} catch (InterruptedException e) {Thread.currentThread().interrupt();}});Thread consumer = new Thread(() -> {try {while (true) {Integer item = queue.take(); // 如果队列为空,则此方法会自动阻塞直到有元素可取System.out.println("Consuming item: " + item);System.out.println("Buffer size after consumption: " + queue.size());Thread.sleep(150); // 模拟消费时间}} catch (InterruptedException e) {Thread.currentThread().interrupt();}});producer.start();consumer.start();producer.join();consumer.join();}
}
实现一个多阶段流水线作业

对于多阶段流水线作业,可以考虑使用java.util.concurrent.Phaser或自定义信号量机制来同步各个阶段之间的执行。每个阶段的任务完成之后通知下一个阶段开始工作。下面给出的是一个简化版本,其中每个阶段都是通过独立线程完成,并且通过共享变量传递数据给下一级别处理。

class PipelineStage implements Runnable {private final Phaser phaser;private final String name;private final Object input;private final Consumer<Object> output;public PipelineStage(Phaser phaser, String name, Object input, Consumer<Object> output) {this.phaser = phaser;this.name = name;this.input = input;this.output = output;}@Overridepublic void run() {phaser.arriveAndAwaitAdvance(); // 等待所有前驱阶段完成System.out.println(name + " processing...");// 模拟处理过程...try { Thread.sleep((long)(Math.random() * 500)); } catch (InterruptedException e) {}if (output != null) output.accept(input); // 将结果传递给下一阶段phaser.arriveAndDeregister(); // 完成本阶段后注销}
}// 测试用例
public class MultiStagePipeline {public static void main(String[] args) throws InterruptedException {Phaser phaser = new Phaser();phaser.register(); // 注册主线程// 创建多个阶段并启动它们PipelineStage stage1 = new PipelineStage(phaser, "Stage 1", "Input Data", null);PipelineStage stage2 = new PipelineStage(phaser, "Stage 2", stage1, null);PipelineStage stage3 = new PipelineStage(phaser, "Stage 3", stage2, result -> System.out.println("Final Output: " + result));new Thread(stage1).start();new Thread(stage2).start();new Thread(stage3).start();phaser.arriveAndAwaitAdvance(); // 等待所有阶段完成}
}
设计一个支持动态调整线程数量的线程池框架

要构建这样一个线程池,我们需要考虑如何监控当前的工作负载并根据需要调整线程的数量。可以通过监听器模式或者轮询的方式来检测任务队列的状态,并相应地增加或减少线程数以优化资源利用率。此外,还需要提供API让用户能够配置核心线程数、最大线程数及队列容量等参数,并允许这些设置在运行时动态更改。

import java.util.concurrent.*;
import java.util.function.Consumer;public class DynamicThreadPoolExecutor extends ThreadPoolExecutor {private final Consumer<DynamicThreadPoolExecutor> configChangeListener;public DynamicThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, Consumer<DynamicThreadPoolExecutor> listener) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);this.configChangeListener = listener;}public synchronized void setCorePoolSize(int corePoolSize) {super.setCorePoolSize(corePoolSize);notifyConfigChange();}public synchronized void setMaximumPoolSize(int maximumPoolSize) {super.setMaximumPoolSize(maximumPoolSize);notifyConfigChange();}private void notifyConfigChange() {if (configChangeListener != null) {configChangeListener.accept(this);}}// 其他必要的方法...
}
分析并修复一段包含潜在死锁风险的代码片段

假设有一段代码如下所示,它展示了两个线程分别试图获取对方持有的锁,从而可能导致死锁的情况。

public class DeadlockExample {private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (lock1) {System.out.println("Thread 1: Holding lock 1...");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (lock2) {System.out.println("Thread 1: Holding lock 1 & 2...");}}});Thread t2 = new Thread(() -> {synchronized (lock2) {System.out.println("Thread 2: Holding lock 2...");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (lock1) {System.out.println("Thread 2: Holding lock 2 & 1...");}}});t1.start();t2.start();}
}

为了避免这种情况的发生,我们应该遵循一定的规则来保证所有的锁请求都按照相同的顺序进行。例如,在上面的例子中,我们可以修改代码使得两个线程总是先尝试获得lock1再尝试获得lock2。这样做可以防止形成循环等待链,进而避免死锁的发生。

探索LockSupport.park()和unpark()的用法

最后,关于LockSupport.park()unpark(),这两个函数提供了非常低级别的线程阻塞/唤醒功能。它们不是基于对象监视器(monitor)而是直接作用于线程本身。这意味着你可以更灵活地控制哪个特定的线程应该被阻塞或唤醒。通常情况下,unpark(Thread thread)会在park()之前调用来“预存”一次许可,这样当目标线程调用park()时可以直接继续执行而不必等待其他线程显式地调用unpark()。此外,值得注意的是,即使多次调用了unpark(),只要有一次对应的park()调用就会消耗掉这个许可;如果再次调用park()而没有新的unpark()调用,则线程将重新进入阻塞状态。

第4章 线程池入门

4.1 ThreadPoolExecutor
4.1.1 创建线程池

在Java中,ThreadPoolExecutor是实现线程池的核心类之一。它允许开发者自定义线程池的行为,包括但不限于核心线程数、最大线程数、空闲线程存活时间等参数。通过这种方式,可以创建更加灵活且适合特定应用场景的线程池。

代码示例:

import java.util.concurrent.*;public class CustomThreadPoolExample {public static void main(String[] args) {// 定义一个简单的任务类class Task implements Runnable {private final String name;public Task(String name) {this.name = name;}@Overridepublic void run() {System.out.println("Executing task " + name);try {Thread.sleep(2000); // 模拟长时间运行的任务} catch (InterruptedException e) {// 如果线程被中断,则恢复中断状态Thread.currentThread().interrupt();System.err.println("Task " + name + " was interrupted.");}System.out.println("Task " + name + " completed.");}}// 创建自定义线程池int corePoolSize = 2; // 核心线程数int maximumPoolSize = 4; // 最大线程数long keepAliveTime = 5000; // 空闲线程存活时间(毫秒)TimeUnit unit = TimeUnit.MILLISECONDS;BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10); // 任务队列容量为10ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 使用默认线程工厂RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); // 拒绝策略ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);// 提交多个任务给线程池执行for (int i = 1; i <= 6; i++) {executor.submit(new Task("Task-" + i));}// 关闭线程池...executor.shutdown();// 尝试优雅地关闭线程池,等待所有任务完成try {if (!executor.awaitTermination(800, TimeUnit.MILLISECONDS)) {System.out.println("Not all tasks have been completed within the timeout period.");}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

这段代码展示了如何使用ThreadPoolExecutor来创建一个具有特定配置的线程池,并向其中提交若干个任务进行异步处理。注意这里选择了LinkedBlockingQueue作为任务队列,并设置了合理的大小以避免无限制的增长;同时指定了CallerRunsPolicy作为拒绝策略,这意味着当线程池无法接受新任务时,调用者所在的线程将会执行该任务。

4.1.2 关闭线程池

正确地关闭线程池非常重要,因为它不仅涉及到资源的有效释放,还可能影响到应用程序的整体性能和稳定性。根据需求的不同,可以选择调用shutdown()shutdownNow()方法来停止线程池的工作。

  • shutdown():此方法会等待所有已提交的任务完成之后再终止线程池,但不再接受新的任务提交。
  • shutdownNow():尝试立即停止所有正在执行中的任务,并返回未开始执行的任务列表。需要注意的是,这并不保证所有活动线程都能被成功中断。

此外,还可以结合awaitTermination()一起使用,以便在线程池完全结束前让程序等待一段时间。如果超出了指定的时间而线程池仍未关闭,则可以通过逻辑判断是否需要采取进一步措施。

代码补充:

// 在main方法末尾添加以下代码,确保线程池能够正常关闭
if (!executor.isTerminated()) {System.out.println("Shutting down Executor Service");
}// 或者使用更激进的方式,立即尝试关闭所有任务
List<Runnable> unfinishedTasks = executor.shutdownNow();
if (!unfinishedTasks.isEmpty()) {System.out.println("Unfinished tasks: " + unfinishedTasks.size());
}
4.2 Executor接口

Executor接口是最基础的执行器接口,它提供了一个简化了多线程编程的方式——将任务提交与任务执行分离出来。具体来说,它仅包含一个名为execute(Runnable command)的方法,用于接收实现了Runnable接口的对象,并安排它们在一个合适的线程上运行。

4.3 ExecutorService 接口
4.3.1 Callable返回任务执行结果

ExecutorService扩展了Executor接口的功能,增加了对Callable的支持,使得我们可以从非阻塞的任务中获取返回值。Callable类似于Runnable,但它允许抛出受检异常并且支持返回类型。为了得到Callable的结果,通常我们会使用submit()方法提交任务,并通过返回的Future对象查询结果或取消任务。

代码示例:

import java.util.concurrent.*;public class CallableExample {public static void main(String[] args) throws InterruptedException, ExecutionException {// 创建一个固定大小的线程池ExecutorService executor = Executors.newFixedThreadPool(2);// 定义一个有返回值的任务Callable<String> task = () -> {Thread.sleep(1000); // 模拟任务执行时间return "Hello from Callable!";};// 提交任务并获取Future对象Future<String> future = executor.submit(task);// 获取任务执行结果String result = future.get(); // 此处会阻塞直到任务完成System.out.println(result);// 关闭线程池executor.shutdown();}
}
4.3.2 .shutdown与shutdownNow

如前所述,shutdown()方法可以让线程池进入“优雅关闭”状态,即不再接受新的任务,但是会继续处理已经提交的任务直到它们全部完成。相比之下,shutdownNow()则试图立即停止所有正在进行的任务,并清理掉尚未启动的任务。

4.4 Executors 工具箱

Executors是一个静态工具类,提供了几种常用的线程池创建方法。这些方法简化了线程池的初始化过程,让用户不必每次都手动设置复杂的构造参数。常见的有:

  • newCachedThreadPool():创建一个可根据需要动态调整大小的线程池,适用于执行大量短期生存期的任务。
  • newFixedThreadPool(int nThreads):创建一个固定大小的线程池,适合于控制并发级别的情况。
  • newSingleThreadExecutor():创建只有一个工作线程的线程池,确保所有任务按照顺序依次执行。
  • newScheduledThreadPool(int corePoolSize):创建一个支持定时调度功能的线程池。
  • newWorkStealingPool(int parallelism):创建一个工作窃取线程池,旨在提高多核处理器上的任务并行度。

代码示例:

import java.util.concurrent.*;public class ExecutorsExample {public static void main(String[] args) {// 创建不同类型的线程池ExecutorService cachedPool = Executors.newCachedThreadPool();ExecutorService fixedPool = Executors.newFixedThreadPool(3);ExecutorService singlePool = Executors.newSingleThreadExecutor();ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);ExecutorService workStealingPool = Executors.newWorkStealingPool();// 提交任务给不同的线程池Runnable task = () -> System.out.println("Task executed by " + Thread.currentThread().getName());cachedPool.submit(task);fixedPool.submit(task);singlePool.submit(task);scheduledPool.schedule(task, 2, TimeUnit.SECONDS);workStealingPool.submit(task);// 关闭线程池cachedPool.shutdown();fixedPool.shutdown();singlePool.shutdown();scheduledPool.shutdown();workStealingPool.shutdown();}
}
4.5 线程工厂与线程组

这部分内容涉及到了更底层的线程管理机制。线程工厂(ThreadFactory)接口允许我们自定义线程的创建方式,比如命名规则、优先级设定等。而线程组(ThreadGroup)则是用来组织相关线程的一种结构,在某些情况下可以帮助更好地管理和监控线程集合。

代码示例:

import java.util.concurrent.*;public class ThreadFactoryExample {public static void main(String[] args) {// 自定义线程工厂ThreadFactory customThreadFactory = runnable -> {Thread thread = new Thread(runnable, "CustomThread-" + Thread.activeCount());thread.setDaemon(true); // 设置为守护线程thread.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级return thread;};// 使用自定义线程工厂创建线程池ExecutorService executor = Executors.newFixedThreadPool(2, customThreadFactory);// 提交任务Runnable task = () -> System.out.println("Running in " + Thread.currentThread().getName());executor.submit(task);// 关闭线程池executor.shutdown();}
}
4.6 线程池异常处理

当线程池中的任务抛出未捕获的异常时,默认情况下JVM会打印堆栈跟踪信息并将线程标记为失败状态。为了避免这种情况,可以为每个线程设置一个UncaughtExceptionHandler,或者直接利用Future.get()捕获由Callable产生的异常。

代码示例:

import java.util.concurrent.*;public class ExceptionHandlingExample {public static void main(String[] args) {// 创建线程池并设置未捕获异常处理器ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);executor.setUncaughtExceptionHandler((t, e) -> System.err.println("Thread " + t.getName() + " threw exception: " + e.getMessage()));// 提交可能会抛出异常的任务executor.submit(() -> {throw new RuntimeException("Something went wrong!");});// 使用Future捕获Callable产生的异常Future<?> future = executor.submit(() -> {throw new Exception("Another issue occurred.");});try {future.get(); // 此处会抛出ExecutionException包装原始异常} catch (InterruptedException | ExecutionException e) {System.err.println("Caught exception from Future: " + e.getCause().getMessage());}// 关闭线程池executor.shutdown();}
}
4.7 本章习题

请参照章节开头给出的例子和说明,尝试编写一些简单的练习来加深理解,例如实现生产者-消费者模式、构建多阶段流水线作业等。此外,也可以探索更多关于LockSupport.park()/unpark()的知识点,了解它们是如何帮助实现低级别的线程同步操作的。

相关文章:

Java多线程与线程池技术详解(四)

接受失败&#xff1a;“失败是什么&#xff1f;没有什么&#xff0c;只是更走近成功一步&#xff1b;成功是什么&#xff1f;就是走过了所有通向失败的路&#xff0c;只剩下一条路&#xff0c;那就是成功的路。”这句话很好地诠释了如何看待失败的问题&#xff0c;即每一次跌倒…...

树莓派开发笔记

一. 登录方式 1.1 方式一:HDMI视频线 1.2 方式二:串口 查看串口有否被加密,默认情况下树莓派串口和蓝牙连接,需先断开蓝牙连接,串口才能用于数据通信。 1.2.1 如何使用串口登录 打开SD卡根目录的"config.txt"文件,将以下内容添加在最后并且保存。这样就停止…...

【数据结构】遍历二叉树

遍历二叉树的算法描述&#xff08;递归定义&#xff09; 先序遍历 若二叉树为空&#xff0c;则空操作&#xff1b; 否则 &#xff08;1&#xff09;访问根节点 &#xff08;2&#xff09;先序遍历左子树 &#xff08;3&#xff09;先序遍历右子树 中序遍历 若二叉树为空…...

嵌入式蓝桥杯学习7 产生PWM

Cubemx配置 打开cubemx&#xff0c;前面的配置看上文&#xff0c;这里主要配置定时器产生PWM波。 以PA1的TIM2-CH2通道为例进行演示。 1.在Timers中打开TIM2,将Channel2配置为PWM Generation CH2。 2.将Clock Source 选择为Internal Clock。 3.配置Paramater Settings中的参…...

档案学实物

档案工作 档案工作的性质 服务性 文化性 管理性 政治性 科学性 档案工作的地位 档案工作的效益 社会性&#xff0c;隐蔽性&#xff0c;滞后性 档案工作的发展规律 档案收集 档案收集工作的内容意义 档案收集工作的具体要求 档案室的档案收集工作 档案馆的档案收集工作 档案…...

数据清洗代码:缺失值,异常值,离群值Matlab处理

目录 基本介绍程序设计参考资料基本介绍 一、过程概述 本过程适用于处理SCADA系统采集到的数据,以及具有类似需求的数据集。处理步骤包括缺失值处理、异常值处理和离群值处理,旨在提升数据质量,增强数据的相关性,同时保持数据的原始特征和随机性。 二、缺失值处理 对于SC…...

Windows设备go环境安装配置

一、下载go安装包 官网链接&#xff1a;All releases - The Go Programming Language (google.cn) 安装过程比较简单&#xff0c;这里不再赘述&#xff0c;可参考这位博主的文章。本文重点在环境配置。golang环境详细安装、配置_golang安装-CSDN博客 二、环境变量配置 1.添…...

导体、半导体和绝缘体

半导体可以根据不同的组合去改变电阻&#xff0c;所以可以用来制作芯片。...

shell 6 if条件判断与for循环结构 (泷羽sec)

声明 学习视频来自B站UP主 泷羽sec,如涉及侵泷羽sec权马上删除文章。 笔记只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 这节课旨在扩大自己在网络安全方面的知识面&#xff0c;了解网络安全领域的见闻&#xff0c;了…...

MetaGPT 安装

1. 创建环境 conda create -n metagpt python3.10 && conda activate metagpt2. 可编辑方式安装 git clone --depth 1 https://github.com/geekan/MetaGPT.git cd MetaGPT pip install -e .3. 配置 metagpt --init-config运行命令&#xff0c;在C盘位置C:\Users\325…...

论文阅读:Single-cell transcriptomics of 20 mouse organs creates a Tabula Muris

The Tabula Muris Consortium., Overall coordination., Logistical coordination. et al. Single-cell transcriptomics of 20 mouse organs creates a Tabula Muris. Nature 562, 367–372 (2018). 论文地址&#xff1a;https://doi.org/10.1038/s41586-018-0590-4 代码地址…...

图生3d 图生全景 学习笔记

目录 instantsplat Aluciddreamer ZoeDepth 会自动下载模型&#xff1a; 图生全景图SD-T2I-360PanoImage&#xff1a; instantsplat Sparse-view SfM-free Gaussian Splatting in Seconds 稀疏视图无SfM高斯喷洒 GitHub - NVlabs/InstantSplat: InstantSplat: Sparse-vi…...

分库分表—4.数据迁移系统文档

大纲 1.数据库设计 2.枚举类 3.接⼝设计 4.定时任务设计 (1)定时核对校验数据的定时任务 (2)数据量统计定时任务 (3)增量数据落地定时任务 (4)失败重试定时任务 5.技术亮点 (1)滚动拉取方案 (2)巧妙的统计滚动进度方案 (3)防止增量同步数据丢失和高效写入方案 (4)…...

HAMR技术进入云存储市场!

2024年12月3日&#xff0c;Seagate宣布其Mozaic 3系列HAMR&#xff08;热辅助磁记录&#xff09;硬盘获得了来自一家领先云服务提供商&#xff08;可能AWS、Azure或Google Cloud其中之一&#xff09;以及其他高容量硬盘客户的资格认证。 Seagate的Mozaic 3技术通过引入热辅助磁…...

Vulnhub---kioptirx5 超详细wp

个人博客 WuTongSec 欢迎大佬指点 打点 nmap 192.168.128.0/24 -sP 找ip nmap 192.168.128.137 --min-rate 10000 -p- 简单全端口扫描 nmap 192.168.128.137 -sC -sV -O -sT 详细 脚本 版本 系统 扫描 dirsearch -u http://192.168.128.137 目录扫描 PORT S…...

单片机状态机实现多个按键同时检测单击、多击、长按等操作

1.背景 在之前有个项目需要一个或多个按键检测&#xff1a;单击、双击、长按等操作 于是写了一份基于状态机的按键检测&#xff0c;分享一下思路 2.实现效果 单击翻转绿灯电平 双击翻转红灯电平 长按反转红绿灯电平 实现状态机检测按键单击&#xff0c;双击&#xff0c;长…...

oracle之用户的相关操作

&#xff08;1&#xff09;创建用户(sys用户下操作) 简单创建用户如下&#xff1a; CREATE USER username IDENTIFIED BY password; 如果需要自定义更多的信息&#xff0c;如用户使用的表空间等&#xff0c;可以使用如下&#xff1a; CREATE USER mall IDENTIFIED BY 12345…...

黑马redis

Redis的多IO线程只是用来处理网络请求的,对于读写操作命令Redis仍然使用单线程来处理 Redisson分布式锁实现15问 文章目录 主线程和IO线程是如何协作的Unix网络编程中的五种IO模型Linux世界一切皆文件生产上限制keys *、flushdb、flushall等危险命令keys * 遍历查询100W数据花…...

HCIA-Access V2.5_1_2 PON技术的特点、优势与典型应用

PON接入技术优势 它的接入方式有两种&#xff0c;点到点光接入和点到多点光接入。 点到点 PON口的资源被一个用户独占&#xff0c;该用户可以享受到更好的带宽体验&#xff0c;同时故障好排查&#xff0c;出现问题&#xff0c;重点检测这一条链路以及终端用户&#xff0c;同…...

css部分

前面我们学习了HTML&#xff0c;但是HTML仅仅只是做数据的显示&#xff0c;页面的样式比较简陋&#xff0c;用户体验度不高&#xff0c;所以需要通过CSS来完成对页面的修饰&#xff0c;CSS就是页面的装饰者&#xff0c;给页面化妆&#xff0c;让它更好看。 1 层叠样式表&#…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...

django blank 与 null的区别

1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是&#xff0c;要注意以下几点&#xff1a; Django的表单验证与null无关&#xff1a;null参数控制的是数据库层面字段是否可以为NULL&#xff0c;而blank参数控制的是Django表单验证时字…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...

Java多线程实现之Runnable接口深度解析

Java多线程实现之Runnable接口深度解析 一、Runnable接口概述1.1 接口定义1.2 与Thread类的关系1.3 使用Runnable接口的优势 二、Runnable接口的基本实现方式2.1 传统方式实现Runnable接口2.2 使用匿名内部类实现Runnable接口2.3 使用Lambda表达式实现Runnable接口 三、Runnabl…...

五、jmeter脚本参数化

目录 1、脚本参数化 1.1 用户定义的变量 1.1.1 添加及引用方式 1.1.2 测试得出用户定义变量的特点 1.2 用户参数 1.2.1 概念 1.2.2 位置不同效果不同 1.2.3、用户参数的勾选框 - 每次迭代更新一次 总结用户定义的变量、用户参数 1.3 csv数据文件参数化 1、脚本参数化 …...