当前位置: 首页 > 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 层叠样式表&#…...

【TCP 网络通信(发送端 + 接收端)实例 —— Python】

TCP 网络通信&#xff08;发送端 接收端&#xff09;实例 —— Python 1. 引言2. 创建 TCP 服务器&#xff08;接收端&#xff09;2.1 代码示例&#xff1a;TCP 服务器2.2 代码解释&#xff1a; 3. 创建 TCP 客户端&#xff08;发送端&#xff09;3.1 代码示例&#xff1a;TCP…...

LSTM+改进的itransformer时间序列预测模型代码

代码在最后 本次设计了一个LSTM基于差分多头注意力机制的改进的iTransformer时间序列预测模型结合了LSTM&#xff08;长短期记忆网络&#xff09;和改进版的iTransformer&#xff08;差分多头注意力机制&#xff09;&#xff0c;具备以下优势&#xff1a; 时序特征建模能力&am…...

Apache-HertzBeat 开源监控默认口令登录

0x01 产品描述: HertzBeat(赫兹跳动) 是一个开源实时监控系统,无需Agent,性能集群,兼容Prometheus,自定义监控和状态页构建能力。HertzBeat 的强大自定义,多类型支持,高性能,易扩展,希望能帮助用户快速构建自有监控系统。0x02 漏洞描述: HertzBeat(赫兹跳动) 开源实时…...

Delete Number

翻译&#xff1a; 主要思路解释 整体思路概述&#xff1a; 本题的目标是给定整数&#xff08;要删除的数字个数&#xff09;和整数&#xff08;以字符串形式表示的数字&#xff09;&#xff0c;通过合理删除个数字&#xff0c;使得最终得到的新数字最小。程序采用了一种贪心算…...

Linux常用快捷键

目录 ​编辑 剪切/复制/粘贴/删除等快捷键 终端及标签页快捷键 历史命令快捷键 移动光标快捷键 控制命令 剪切/复制/粘贴/删除等快捷键 快捷键 功能 ShiftCtrlC 复制 ShiftCtrlV 粘贴 CtrlInsert 复制命令行内容 ShiftInsert 粘贴命令行内容 Ctrlk 剪切&#…...

针对xpath局限的解决方案

上篇《网页数据提取利器 -- Xpath》我们对xpath的介绍中提到了xpath的几点局限性&#xff1a; 结构依赖性强性能动态网页支持不足 本篇是针对这些局限提出的解决方案和补充方法&#xff0c;以提升 XPath 的实用性和适应性。 1. 动态网页的处理 局限&#xff1a; XPath 无法…...

深入解析 HTML Input 元素:构建交互性表单的核心

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

ffmpeg转码与加水印

文章目录 转码 与加水印引入jar包代码ffmpeg安装错误解决方法 转码 与加水印 引入jar包 <dependency><groupId>net.bramp.ffmpeg</groupId><artifactId>ffmpeg</artifactId><version>0.6.2</version></dependency>代码 impo…...

Leetcode 104. 二叉树的最大深度(Java-深度遍历)

题目描述&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;…...

阳明心学-传习录学习总结

资料 王阳明介绍&#xff1a;明代杰出的思想家、军事家、教育家&#xff1b;自刑部主事历任贵州龙场驿丞、庐陵知县、右佥都御史、南赣巡抚、两广总督等职&#xff0c;接连平定南赣、两广盗乱及宸濠之乱&#xff0c;因功获封“新建伯”&#xff0c;成为明代因军功封爵的三位文…...