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

java中创建多线程的4种方式

目录

一、继承 Thread 类创建线程

步骤

示例代码

原理

二、实现 Runnable 接口创建线程

步骤

示例代码

原理

三、实现 Callable 接口创建线程

步骤

示例代码

原理

与Runnable接口相比的不同之处

四、使用线程池创建线程

步骤

示例代码(使用 Executors.newFixedThreadPool)

原理

线程池的优势

自定义线程池

五、总结


        在 Java 编程中,多线程是一项非常重要的技术,它能够充分利用计算机的多核处理器资源,提高程序的执行效率和响应性。本文将详细介绍 Java 中创建多线程的四种方式,包括继承 Thread 类、实现 Runnable 接口、实现 Callable 接口以及使用线程池,并对每种方式的原理、代码示例和适用场景进行深入剖析。

一、继承 Thread 类创建线程

步骤

  • 创建一个类继承自 Thread 类。
  • 重写 run 方法,在 run 方法中定义线程要执行的任务。
  • 创建该类的实例,然后调用 start 方法启动线程。

示例代码

class MyThread extends Thread{// Ctrl + o// 展示所有的可以重写的方法@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("数据:"+i);}}
}
public class Demo01 {/***  第一种方案,继承Thread类  重写run方法 实现* @param args*/public static void main(String[] args) {// 在Main方法中,启动了一个子线程,子线程什么时候工作MyThread thread = new MyThread();thread.start();// 启动一个线程,调用start方法,不要调用run方法// 一个线程类,是可以创建多个不同的子线程的MyThread thread2 = new MyThread();thread2.start();// 启动一个线程,调用start方法,不要调用run方法// 主线程,直接运行代码   会出现子线程和主线程抢占资源的情况for (int i = 10; i < 100; i++) {System.err.println("Main:"+i);}}
}

原理

        当调用 start 方法时,会在新的线程中执行 run 方法。需要注意的是,start 方法只是启动线程,不会立即执行 run 方法。线程要等待获取 CPU 资源后才会执行 run 方法,而且在执行过程中可能会被其他线程抢占 CPU 资源。

二、实现 Runnable 接口创建线程

步骤

  • 创建一个类实现 Runnable 接口。
  • 实现 run 方法,在 run 方法中定义线程要执行的任务。
  • 创建 Runnable 接口实现类的实例,将其作为参数传递给 Thread 类的构造函数,然后调用 start 方法启动线程。

示例代码

class A implements Runnable{@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}
}
public class Demo03 {/***  多线程创建的第二种方式,使用 Runnable接口*  该接口还需要传递给Thread类才能启动,否则自己启动不了**  两种方式:推荐使用第二种*   1、Thread类是一个线程类,它只需要管理好线程就行了,不需要管业务怎么写*   2、具体的业务可以交给Runnable接口实现*   3、java是单继承的,继承了Thread,就无法继承别的类了,但是可以实现多个接口。*/public static void main(String[] args) {A a = new A();new Thread(a).start();// Runnable接口本身就是一个函数式接口,就可以使用lambda表达式,代码可以简化为如下:new Thread( ()-> {for (int i = 0; i < 1000; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}).start();for (int i = 0; i < 1000; i++) {System.err.println(Thread.currentThread().getName()+":"+i);}}
}

原理

        Runnable 接口定义了一个无返回值的 run 方法,用于包含线程要执行的代码。Thread 类的构造函数可以接收一个 Runnable 接口的实现对象,当调用 Thread 的 start 方法时,会在新的线程中执行 Runnable 对象的 run 方法。这种方式比继承 Thread 类更灵活,因为 Java 是单继承的,如果一个类已经继承了其他类,就不能再继承 Thread 类了,但是可以实现 Runnable 接口来实现多线程。

三、实现 Callable 接口创建线程

步骤

  • 创建一个类实现 Callable 接口,该接口是一个函数式接口,有一个泛型参数,用于指定返回值类型。
  • 实现 call 方法,在 call 方法中定义线程要执行的任务,并返回一个结果。
  • 创建 Callable 接口实现类的实例,将其包装在一个 FutureTask 对象中,FutureTask 实现了 RunnableFuture 接口,而 RunnableFuture 接口继承了 Runnable 和 Future 接口。
  • 将 FutureTask 对象作为参数传递给 Thread 类的构造函数,然后调用 start 方法启动线程。可以通过 FutureTask 的 get 方法获取 call 方法的返回结果。

示例代码

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;class MyCall implements Callable<Integer>{@Overridepublic Integer call() throws Exception {return 200;}
}class MyRun implements Runnable{@Overridepublic void run() {System.out.println("我是子线程....");}
}
public class Demo08 {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<Integer> futureTask = new FutureTask<>(new MyCall());new Thread(futureTask,"计算线程").start();Integer i = futureTask.get();System.out.println(i);// --------------------------------------new Thread(new MyRun()).start();// ------------------使用callable 模拟 子线程进行大量计算并返回结果------------------FutureTask<Integer> f1 = new FutureTask<>(()->{System.out.println(Thread.currentThread().getName()+"  come in callable");TimeUnit.SECONDS.sleep(4);return 1024;});FutureTask<Integer> f2 = new FutureTask<>(()->{System.out.println(Thread.currentThread().getName()+"  come in callable");TimeUnit.SECONDS.sleep(4);return 2048;});new Thread(f1,"线程一:").start();new Thread(f2,"线程二:").start();while(!f1.isDone()){System.out.println("f1  wait中.....");}while(!f2.isDone()){System.out.println("f2  wait中.....");}// 其实 get 获取不到值会一直阻塞,直到获取到值为止int a = f1.get();int b = f2.get();System.out.println(a+b);}
}

原理

        Callable 接口与 Runnable 接口类似,但是 Callable 接口的 call 方法可以有返回值,并且可以抛出异常。FutureTask 用于包装 Callable 对象,它可以在未来某个时刻获取 call 方法的返回结果。通过这种方式,可以实现有返回值的多线程任务。

与Runnable接口相比的不同之处

(1)是否有返回值

(2)是否抛异常
(3)落地方法不一样,一个是run,一个是call

四、使用线程池创建线程

步骤

  • 通过 Executors 工具类的静态方法(如 newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutor)创建一个线程池对象,或者直接使用 ThreadPoolExecutor 类来创建自定义的线程池。
  • 创建 Runnable 或 Callable 接口实现类的实例,作为任务提交给线程池。对于 Runnable 任务,可以使用 execute 方法提交;对于 Callable 任务,可以使用 submit 方法提交。

示例代码(使用 Executors.newFixedThreadPool

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo {public static void main(String[] args) {//       ExecutorService threadPool =  Executors.newFixedThreadPool(5); //一个银行网点,5个受理业务的窗口
//       ExecutorService threadPool =  Executors.newSingleThreadExecutor(); //一个银行网点,1个受理业务的窗口ExecutorService threadPool =  Executors.newCachedThreadPool(); //一个银行网点,可扩展受理业务的窗口//10个顾客请求try {for (int i = 1; i <=10; i++) {threadPool.execute(()->{System.out.println(Thread.currentThread().getName()+"\t 办理业务");});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}
}

原理

        线程池用于管理和复用线程。当提交一个任务到线程池时,线程池会根据自身的状态和配置来决定如何处理任务。如果线程池中有空闲线程,就会将任务分配给空闲线程执行;如果没有空闲线程且线程数量未达到最大限制,就会创建新的线程来执行任务;如果线程数量达到最大限制且任务队列已满,会根据线程池的拒绝策略来处理任务。这样可以有效地控制线程的数量,提高系统的性能和资源利用率,减少线程创建和销毁的开销。

线程池的优势

线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

它的主要特点为:线程复用;控制最大并发数;管理线程。

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗。

第二:提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

Java中的线程池是通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个类。

经常使用的线程池做法

1、Executors.newFixedThreadPool(int)

执行长期任务性能好,创建一个线程池,一池有N个固定的线程,有固定线程数的线程

newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的是LinkedBlockingQueue

2、Executors.newSingleThreadExecutor()

一个任务一个任务的执行,一池一线程。

newSingleThreadExecutor 创建的线程池corePoolSize和maximumPoolSize值都是1,它使用的是LinkedBlockingQueue

3、Executors.newCachedThreadPool()

执行很多短期异步任务,线程池根据需要创建新线程,但在先前构建的线程可用时将重用它们。可扩容,遇强则强。

newCachedThreadPool创建的线程池将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

自定义线程池


虽然根据API 我们能很轻松的使用到线程池,但是在实际开发中我们经常自定义线程池,怎么做呢?

参数说明

1、corePoolSize:线程池中的常驻核心线程数
2、maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1
3、keepAliveTime:多余的空闲线程的存活时间   
当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时, 多余线程会被销毁直到只剩下corePoolSize个线程为止。
4、unit:keepAliveTime的单位 
5、workQueue:任务队列,被提交但尚未被执行的任务  就是我们之前讲的阻塞队列
6、threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认的即可
7、handler:拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝
请求执行的runnable的策略

线程池的拒绝策略

AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行。
CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,俗称从哪儿来到哪儿去。
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加人队列中尝试再次提交当前任务。
DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种策略。

以上内置拒绝策略均实现了RejectedExecutionHandle接口

1、在创建了线程池后,线程池中的线程数为零。

2、当调用execute()方法添加一个请求任务时,线程池会做出如下判断:

        2.1如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;

        2.2如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;

        2.3如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

        2.4如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。

3、当一个线程完成任务时,它会从队列中取下一个任务来执行。

4、当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:

        如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。

        所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。

 示例代码

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
/*** 线程池* Arrays* Collections* Executors*/
public class MyThreadPoolDemo {public static void main(String[] args) {ExecutorService threadPool = new ThreadPoolExecutor(2,5,2L,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(3),Executors.defaultThreadFactory(),//new ThreadPoolExecutor.AbortPolicy()//new ThreadPoolExecutor.CallerRunsPolicy()//new ThreadPoolExecutor.DiscardOldestPolicy()new ThreadPoolExecutor.DiscardOldestPolicy());//10个顾客请求try {for (int i = 1; i <= 10; i++) {threadPool.execute(() -> {System.out.println(Thread.currentThread().getName() + "\t 办理业务");});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}
}

五、总结

        Java 提供了多种创建多线程的方式,每种方式都有其特点和适用场景。继承 Thread 类简单直接,适用于简单的线程任务;实现 Runnable 接口更加灵活,适合在已有类层次结构中使用多线程;实现 Callable 接口可用于需要获取线程执行结果的场景;使用线程池则可以高效地管理和复用线程,适用于需要大量线程处理任务的情况,并且可以通过合理配置线程池参数来优化系统性能。在实际开发中,需要根据具体的需求和场景选择合适的多线程创建方式,以充分发挥多线程编程的优势,提高程序的质量和效率。

相关文章:

java中创建多线程的4种方式

目录 一、继承 Thread 类创建线程 步骤 示例代码 原理 二、实现 Runnable 接口创建线程 步骤 示例代码 原理 三、实现 Callable 接口创建线程 步骤 示例代码 原理 与Runnable接口相比的不同之处 四、使用线程池创建线程 步骤 示例代码&#xff08;使用 Executo…...

MATLAB深度学习(二)——如何训练一个卷积神经网路

2.1 基本概念 从数学的角度看&#xff0c;机器学习的目标是建立输入和输出的函数关系&#xff0c;相当于 y F&#xff08;x&#xff09;的过程。F&#xff08;x&#xff09;就是我们所说的模型&#xff0c;对于使用者来说&#xff0c;这个模型就是一个黑箱&#xff0c;我们不知…...

删除k8s 或者docker运行失败的脚本

vi delete_exited_containers.sh#!/bin/bash# 列出所有停止的容器并存储到数组 list_exited_containers() {echo -e "\nStopped containers:"containers()# 获取停止的容器信息并存入数组while IFS read -r line; docontainers("$line")done < <(do…...

重置docker版本的octoprint管理员账号密码

我的情况是octoprint安装在HiNAS系统的机顶盒上&#xff0c;只有一个账号&#xff0c;但是忘记了用户名和密码。有两个选择&#xff1a; 可以试试先找回用户名&#xff0c;然后尝试你的常用密码。直接重置所有账号。 1.找回用户名&#xff1a; 使用使用 docker exec -it <…...

prometheus监控数据远程写入Kafka集群

文章目录 前言一、环境简介1.1 环境简介1.2 部署清单1.3 组件版本 二、部署步骤2.1 prometheus部署2.2 kafka集群部署2.3 prometheus-kafka-adapter部署 三、数据验证四、总结 前言 根据项目要求&#xff0c;需将prometheus监控数据存储到kafka中。前面为了图方便就搭建了单机…...

Excel使用-弹窗“此工作簿包含到一个或多个可能不安全的外部源的链接”的发生与处理

文章目录 前言一、探讨问题发生原因1.引入外部公式2.引入外部数据验证二、问题现象排查及解决1.排查公式2.排查数据验证3.特殊处理方式总结前言 作为一种常用的办公软件,Excel被大家所熟知。尽管使用了多年,有时候在使用Excel时候也会发生一些不太常见的现象,需要用心核查下…...

C++小白实习日记——Day 2 TSCNS怎么读取当前时间

和老板问了一下&#xff0c;今天就可以自己上手了&#xff1a; 用TSCNS写了一个cpp,运行出来老板说让我去看看另一个项目是怎么做的 用TSCNS和std库获取当前时间 #include <iostream> #include <iomanip> #include "tscns.h"using namespace std;TSCN…...

【Pythonr入门第二讲】你好,世界

"Hello, World!" 是一种传统的编程入门示例&#xff0c;通常是程序员学习一门新编程语言时编写的第一个程序。这个程序的目标非常简单&#xff1a;在屏幕上输出 "Hello, World!" 这个字符串。尽管它非常简单&#xff0c;但具有重要的象征意义和实际价值。 …...

3D Streaming 在线互动展示系统:NVIDIA RTX 4090 加速实时渲染行业数字化转型

随着科技的飞速发展&#xff0c;实时渲染正逐步成为游戏与实时交互领域的重要驱动力。与离线渲染不同&#xff0c;实时渲染需要极高的计算性能&#xff0c;对硬件设备尤其是GPU的性能要求极高。随着 RTX 4090 显卡的问世&#xff0c;其强大的算力和创新技术&#xff0c;为实时渲…...

Oracle 单机及 RAC 环境 db_files 参数修改

Oracle 数据库中 DB_FILES 定义了数据库中数据文件的个数&#xff0c;默认值为200&#xff0c;如果创建数据库文件时超过DB_FILES 定义的值就会报 ORA-00059 错误。 下面分别演示单机及 RAC 环境下修改 db_files 参数的操作步骤。 一、单机环境 1.查询当前参数值 SQL> sh…...

消息中间件分类

消息中间件&#xff08;Message Middleware&#xff09;是一种在分布式系统中实现跨平台、跨应用通信的软件架构。它基于消息传递机制&#xff0c;允许不同系统、不同编程语言的应用之间进行异步通信。 常见的消息中间件类型包括&#xff1a; 1. JMS&#xff08;Java Message S…...

讯飞、阿里云、腾讯云:Android 语音合成服务对比选择

在 移动端 接入语音合成方面&#xff0c;讯飞和腾讯云等都是优秀的选择&#xff0c;但各有其特点和优势。咱们的需求是需要支持普通话/英语/法语三种语言&#xff0c;以下是对各个平台的详细比较&#xff1a; 一、讯飞语音合成介绍 与语音听写相反&#xff0c;语音合成是将一段…...

SpringBoot开发——整合AJ-Captcha实现安全高效的滑动验证码

文章目录 一、什么是AJ-Captcha二、项目配置1、Maven依赖配置2、滑动验证码的基本原理3、 后端实现3.1 生成滑动验证码图片代码解释3.2 校验滑块位置代码解释4、前端部分代码解释5、Redis 缓存滑动验证码信息5.1 Redis配置5.2使用Redis缓存验证码数据5.3 校验时从Redis获取总结…...

Spring Security 核心组件

Spring Security 是一个功能全面的安全框架&#xff0c;用于处理基于 Spring 应用程序的身份验证和授权。 它提供了开箱即用的支持&#xff0c;采用行业标准的做法和机制来保护你的应用。 无论你是开发简单的 Web 应用还是复杂的微服务架构&#xff0c;理解 Spring Security …...

聚焦 AUTO TECH 2025华南展:探索新能源汽车发展新趋势

随着“新四化”浪潮的推进&#xff0c;汽车行业正经历前所未有的变革。中国新能源汽车正逐渐走向世界。国内汽车制造巨头如比亚迪、吉利、奇瑞、长安等&#xff0c;已经将出口提升至核心战略地位。中国新能源汽车的发展&#xff0c;不仅推动了全球汽车产业的电动化转型&#xf…...

Python-简单病毒程序合集(一)

前言&#xff1a;简单又有趣的Python恶搞代码&#xff0c;往往能给我们枯燥无味的生活带来一点乐趣&#xff0c;激发我们对编程的最原始的热爱。那么话不多说&#xff0c;我们直接开始今天的编程之路。 编程思路&#xff1a;本次我们将会用到os,paltform,threading,ctypes,sys,…...

[STM32]从零开始的STM32 HAL库环境搭建

一、前言 之前在搭建STM32的标准库环境时就告诉过大家&#xff0c;开发STM32的方式主要有三种。一种是最原始但是效率最高的寄存器开发&#xff0c;另一种是效率仅次于寄存器难度相对较低的标准库开发&#xff0c;最后一种是最为简单但是程序效率最低的HAL库开发。如果对于初学…...

Docker部署Kafka SASL_SSL认证,并集成到Spring Boot

1&#xff0c;创建证书和密钥 需要openssl环境&#xff0c;如果是Window下&#xff0c;下载openssl Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 还需要keytool环境&#xff0c;此环境是在jdk环境下 本案例所使用的账号密码均为&#xff1a; ka…...

Pr:音频过渡

Adobe Premiere Pro 自带一组共三个音频过渡 Audio Transitions效果。 对音频剪辑之间应用交叉淡化 Crossfade过渡&#xff0c;操作方式类似于应用视频过渡效果。 对于交叉淡化&#xff0c;要保证前剪辑的出点之后及后剪辑的入点之前有足够的预留内容&#xff08;也称“手柄”&…...

HarmonyOs鸿蒙开发实战(17)=>沉浸式效果第二种方案一组件安全区方案

1.沉浸式效果的目的 开发应用沉浸式效果主要指通过调整状态栏、应用界面和导航条的显示效果来减少状态栏导航条等系统界面的突兀感&#xff0c;从而使用户获得最佳的UI体验。 2.组件安全区方案介绍 应用在默认情况下窗口背景绘制范围是全屏&#xff0c;但UI元素被限制在安全区内…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...