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

AIVideo一键部署指南:开箱即用的AI视频创作平台

AIVideo一键部署指南&#xff1a;开箱即用的AI视频创作平台 1. 平台概览&#xff1a;从主题到视频的全流程自动化 AIVideo是一款革命性的AI视频创作工具&#xff0c;它能将您的文字主题自动转化为专业级视频作品。想象一下&#xff0c;您只需输入一个简单的想法&#xff0c;比…...

手把手解决Simulink与贝加莱Automation Studio联调的5个典型报错(附详细截图)

手把手解决Simulink与贝加莱Automation Studio联调的5个典型报错&#xff08;附详细截图&#xff09; 在工业自动化领域&#xff0c;Simulink与贝加莱PLC的联合开发已经成为复杂控制系统设计的黄金组合。但当你满怀期待地将精心设计的Simulink模型转换为Automation Studio可执行…...

百川2-13B-4bits模型微调实战:用OpenClaw日志数据提升任务理解力

百川2-13B-4bits模型微调实战&#xff1a;用OpenClaw日志数据提升任务理解力 1. 为什么需要针对OpenClaw任务做模型微调 去年夏天&#xff0c;当我第一次尝试用OpenClaw自动化处理日常工作报告时&#xff0c;发现一个有趣的现象&#xff1a;当我直接说"帮我整理上周的销…...

基于北方苍鹰优化算法优化径向基函数神经网络(NGO - RBF)的时间序列预测

基于北方苍鹰优化算法优化径向基函数神经网络(NGO-RBF)的时间序列预测 NGO-RBF时间序列 优化参数为扩散速度&#xff0c;采用交叉验证防止过拟合 matlab代码注&#xff1a;暂无Matlab版本要求 -- 推荐 2018B 版本及以上在时间序列预测领域&#xff0c;寻找高效准确的模型一直是…...

三大AI-IDE实战:如何用OneCode注解快速生成电商后台管理系统(附避坑指南)

三大AI-IDE实战&#xff1a;如何用OneCode注解快速生成电商后台管理系统&#xff08;附避坑指南&#xff09; 电商后台管理系统作为企业数字化转型的核心枢纽&#xff0c;其开发效率直接影响业务迭代速度。传统开发模式下&#xff0c;表单、列表、权限等模块的重复编码消耗了团…...

JESD204B时钟系统解析——从设备时钟到多帧时钟的协同设计

1. JESD204B时钟系统全景解析 第一次接触JESD204B协议时&#xff0c;我被它复杂的时钟系统搞得晕头转向。这个高速串行接口协议之所以难啃&#xff0c;很大程度上就是因为其独特的时钟架构设计。在实际项目中&#xff0c;我遇到过因为时钟配置不当导致数据传输失败的案例&…...

如何使用LXC实现高效容器编排:管理大规模集群的完整指南

如何使用LXC实现高效容器编排&#xff1a;管理大规模集群的完整指南 【免费下载链接】lxc LXC - Linux Containers 项目地址: https://gitcode.com/gh_mirrors/lx/lxc LXC&#xff08;Linux Containers&#xff09;是一种强大的容器技术&#xff0c;允许用户在单个Linux…...

mmdetection2.11.0实战:如何用VOC和COCO数据集精准计算每个类别的mAP(附避坑指南)

mmdetection2.11.0实战&#xff1a;VOC与COCO数据集mAP计算全解析与避坑指南 在目标检测领域&#xff0c;mAP&#xff08;mean Average Precision&#xff09;是衡量模型性能的核心指标。但不同数据集&#xff08;如VOC和COCO&#xff09;的评估标准差异&#xff0c;常常让研究…...

终极指南:p5.js Web Editor 如何让创意编程触手可及

终极指南&#xff1a;p5.js Web Editor 如何让创意编程触手可及 【免费下载链接】p5.js-web-editor p5.js Web Editor, officially launched! 项目地址: https://gitcode.com/gh_mirrors/p5/p5.js-web-editor 欢迎来到 p5.js Web Editor 的世界&#xff01;这是一个专为…...

SDMatte开源模型部署实录:从镜像拉取到首张图产出完整记录

SDMatte开源模型部署实录&#xff1a;从镜像拉取到首张图产出完整记录 1. 引言 SDMatte是一款面向高质量图像抠图场景的AI模型&#xff0c;特别适合处理主体分离、透明物体提取、边缘精修、商品图去背景等任务。对于玻璃、薄纱、羽毛、叶片等边缘细节复杂或半透明目标&#x…...