多线程知识概述

目录

1. 基本知识概述

2. 多线程概述

2.1 优点

2.2 使用场景

3. 创建线程

3.1 继承 Thread 类

3.2 实现 Runnable 接口

3.3 比较

 3.4 创建 Callable 接口

3.5 使用线程池

 4. Thread 类常用方法

5. 线程生命周期

6. 线程安全机制

6.1 同步代码块

6.2 同步方法

 6.3 Lock 锁

7. 死锁


1. 基本知识概述

程序:一段静态的代码;

进程:正在运行的一个程序,或程序的一次运行过程。

           是一个动态的过程:有产生,存在,消亡的过程,即生命周期;
线程:一个程序内部的一条执行路径;

单核 cpu :在一个时间单元内,只能执行一个线程的任务;

                如高速公路的收费站只有一个工作人员,只能一个一个的收费,没有收费的需要排队等待,但 cpu 时间单元很短,我们感觉不出来;

多核 cpu:相当于老板请了工人,现在的服务器都是多核的;

并行:多个 cpu 同时执行多个任务,如:多人同时做不同的事情;

并发:一个 cpu 同时执行多个任务,如:秒杀:多人同时做一件事情;

2. 多线程概述
2.1 优点

提高应用程序的响应;

提高计算机系统 cpu 的利用率;

改善程序结构。将长而复杂的进程分为多个线程,,独立运行,利于理解和修改;

2.2 使用场景

程序需要同时执行两个或多个任务时;

程序需要实现一些需要等待的任务时;

需要一些后台运行的程序时;

3. 创建线程
3.1 继承 Thread 类

1.创建一个继承于 Thread 类的子类

2.重写 Thread 类的 run() ---> 将此线程执行的操作声明在 run() 中

3.创建 Thread 类子类的对象

4.通过此对象调用 start()

        ①启动当前线程

        ②调用当前线程的 run()

思考一:调用 run()方法行不行?调用后是单线程

思考二:再启动一个线程 * 需要重新创建一个对象

/***多线程的创建:*      方式一:继承于 Thread 类*          1.创建一个继承于 Thread 类的子类*          2.重写 Thread 类的 run() ---> 将此线程执行的操作声明在 run() 中*          3.创建 Thread 类子类的对象*          4.通过此对象调用 start()*//*
主线程创建Thread()子类,并创建对象mythread;
mythread 线程调用 start() 方法,
所以共有两个线程;
*///1.创建一个继承于 Thread 类的子类
public class Mythread extends Thread {public static void main(String[] args) {
//3.创建 Thread 类子类的对象Mythread mythread = new Mythread();
//4.通过此对象调用 start()mythread.start();System.out.println("=====================");for (int i = 0; i < 11; i++) {if (i % 2 == 0){System.out.print(i + " ");}}}
//2.重写 Thread 类的 run()@Overridepublic void run() {//将此线程执行的操作声明在 run() 中for (int i = 0; i < 101; i++) {if (i % 2 == 0){System.out.println(i);}}}
}
3.2 实现 Runnable 接口

1.创建一个实现了 Runnable 接口的类

2.实现类实现 Runnable 中的抽象方法:run ( )

3.创建实现类的对象

4.将此对象作为参数传递到 Thread 类的构造器中,创建 Thread 类的对象

5.通过 Thread 类的对象调用 start()

public class MyThread2 {public static void main(String[] args) {//    3.创建实现类的对象Mythread02 mythread02 = new Mythread02();//    4.将此对象作为参数传递到 Thread 类的构造器中,创建 Thread 类的对象Thread thread = new Thread(mythread02);//    5.通过 Thread 类的对象调用 start()thread.start();//Thread 类的对象调用 start() 才会启动分支线程
//        start() 作用:①:启动线程 ②:调用当前线程的 run()//再创建一个线程Thread thread2 = new Thread(mythread02);thread2.start();}
}//多线程的创建方式二//    1.创建了一个实现了 Runnable 接口的类
class Mythread02 implements Runnable{//    2.实现类实现 Runnable 中的抽象方法:run()@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + " " + i);}}
}
3.3 比较

优先选择实现 Runnable 接口的方式

        1.实现的方式相比类的单继承的局限性要好

        2.实现的方式更适合处理多个线程有共享数据的情况

        相同点:

                两种方式都要重写 run(),且线程执行的操作写在其方法体中;

案例比较:三个窗口买票:

继承 Thread 类的方式:

public class BuyTickets {public static void main(String[] args) {
//        有三个窗口Test test1 = new Test();Test test2 = new Test();Test test3 = new Test();test1.start();test2.start();test3.start();}
}class Test extends Thread{private static int ticket = 100;//static:每个线程共享同一静态变量@Overridepublic void run(){while (true){if(ticket >= 0) {System.out.println(getName() + "票号:" + ticket);ticket --;}else break;}}
}

使用实现 Runnable 接口的方式

//解决三个窗口卖票:使用实现 Runnable 接口的方式
class TicketTest{public static void main(String[] args) {SaleTicket saleTicket = new SaleTicket();
//        相当于三个线程共用一个对象 saleTicketThread thread1 = new Thread(saleTicket);Thread thread2 = new Thread(saleTicket);Thread thread3 = new Thread(saleTicket);thread1.start();thread2.start();thread3.start();}
}class SaleTicket implements Runnable{private int ticket = 100;@Overridepublic void run() {while (true){if(ticket > 0)System.out.println(Thread.currentThread().getName() + "票号:" + ticket);else break;ticket --;}}
}
 3.4 创建 Callable 接口

与实现 Runnable 接口相比,Callable 功能更强大些

如何理解:

        1.call() 有返回值;

        2.call() 可以抛出异常,被外面打操作捕获,获取异常信息;

        3.Callable 支持泛型

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;/*** 与实现 Runnable 接口相比,Callable 功能更强大些*      如何理解:*      1.call() 有返回值;*      2.call() 可以抛出异常,被外面打操作捕获,获取异常信息;*      3.Callable 支持泛型**  创建线程的方式三:实现 Callable 接口  ---JDK 5.0 新增**      步骤:①创建一个实现 Runnable 接口的实现类*           ②实现 call() 方法,将需要执行的操作声明在该方法中*           ③创建 Callable 实现类的对象*           ④将此Callable实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象*           ⑤将FutureTask对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()*           ⑥获取Callable中call方法的返回值*/public class CreateCallum {public static void main(String[] args) {// ③创建 Callable 实现类的对象CreateThread createThread = new CreateThread();//④将此Callable实现类的对象作为参数传递到FutureTask构造器中// ,创建FutureTask的对象FutureTask futureTask = new FutureTask(createThread);//⑤将FutureTask对象作为参数传递到Thread类的构造器中,// 创建Thread对象,并调用start()new Thread(futureTask).start();try {//⑥获取Callable中call方法的返回值//get()返回值即为futureTask构造器参数Callable实现类重写的call()的返回值Object sum = futureTask.get();System.out.println(sum);} catch (InterruptedException e) {throw new RuntimeException(e);} catch (ExecutionException e) {throw new RuntimeException(e);}}
}//①创建一个实现 Runnable 接口的实现类
class CreateThread implements Callable {//②实现 call() 方法,将需要执行的操作声明在该方法中@Overridepublic Object call() throws Exception {int sum = 0;for (int i = 1; i <= 100; i++) {if(i % 2 == 0){System.out.println(i);sum += i;}}return sum;}
}
3.5 使用线程池

创建线程的方式四:使用线程池

        好处:

        1.提高响应速度(减少创新新线程的时间)

        2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)

        3.便于线程管理

                corePoolSize:核心池的大小

                maximumPoolSize:最大线程数

                keepAliveTime:线程没有任务时最多保持多长时间后会终止

初学者简单理解一下线程池的基本概念即可;

 4. Thread 类常用方法
/***测试 Thread 类中常用方法:*  1.start():①启动当前线程;②调用当前线程的 run()*  2.run():(需要重写 Thread类中 run()),将创建线程执行的操作声明在此方法中*  3.currentThread():静态方法,Thread类直接调用。返回执行当前代码的线程*  4.getName():获取当前线程的名字*  5.setName():设置当前线程的名字*  6.yield():释放当前 cpu 的执行权,暂停当前执行的线程,把执行机会让给优先级相同或更高的线程。类似“礼让”*  7.join():在线程 a 中调用线程 b 的 join(),此时线程 a 就进入阻塞状态,直到线程 b*      完全执行完以后,线程 a 才结束阻塞。类似插队,加塞*  8.stop():已过时。强制生命周期结束,不推荐使用*  9.sleep(long millitime):休眠,单位:毫秒*  10.isAlive():判断当前线程是否存活***  1.线程优先级:*      MAX_PRIORITY:10*      NORM_PRIORITY:5 默认优先级*      MIN_PRIORITY:1*  2.如何获取与设置当前线程的优先级:*      getPriority():获取线程的优先级*      setPriority():设置线程的优先级*  说明:高优先级的线程要抢占低优先级 cpu 的执行权,*          但只是从概率上讲,高优先级的线程大概率会被执行;*/public class ThreadMethodTest {public static void main(String[] args) {ThreadMethod threadMethod = new ThreadMethod();threadMethod.setName("分线程");//返回线程名字之前修改名称threadMethod.setPriority(Thread.MAX_PRIORITY);//执行分线程前设置优先级threadMethod.start();
//  以上代码(12-14行)均是主线程执行的,只有 start()执行后才是分线程//给主线程命名Thread.currentThread().setName("主线程");Thread.currentThread().setPriority(Thread.MIN_PRIORITY);for (int i = 1; i < 6; i++) {
//            try {
//                Thread.sleep(10);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }System.out.println(ThreadMethod.currentThread().getName()+":"+i);if(i == 3){try {threadMethod.join();//执行分线程} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
}class ThreadMethod extends Thread{@Overridepublic void run(){isAlive();for (int i = 0; i < 10; i++) {System.out.print(currentThread()+" ");//在 Thread 类里面,“前缀”可以省略System.out.print(getName()+" ");//在 Thread 类里面,“前缀”可以省略System.out.println(i+" ");if( i % 2 == 0) this.yield();}}
}
5. 线程生命周期

6. 线程安全机制

解决线程安全的方式有两种:同步代码块,同步方法

6.1 同步代码块

实现 Runnable 接口创建线程的解决线程安全方式;

/*** 举例:卖票过程中,创建三个窗口卖票,总票数为 100,使用实现 Runnable 接口的方式使用线程*      1.问题:卖票过程中出现重票,错票 ——> 线程的安全问题*      2.原因:当某个线程操作车票的过程中,操作尚未完成时,其他线程也参与进来*      3.解决方法:当一个线程 A 在操作 ticket 的时候,其他线程不能参与进来,*          直到线程 A 操作完 ticket 时,其他线程才可以开始操作 ticket;*          即使线程 A 出现了阻塞,也不能被改变;**     有共享数据,线程才会存在安全问题;*          (实现类中天然是一个共享的数据,因为只造了一个对象)*          (继承的方式中早了多个对象,类有时需要 static 修饰)**     在 Java 中,通过同步机制,解决线程的安全问题。**     方法一:同步代码块:*          synchronized(同步监视器){*              //需要被同步的代码*          }*          说明:1.操作共享数据的代码,即为需要被同步的代码;*                  共享数据:多个线程共同操作的变量,比如:ticket 就是共享数据*               2.同步监视器:俗称,锁*                      任何一个类的对象,都可以充当锁*                      注意创建对象的位置*               3.多个线程必须要公用同一把锁**              4.同步的方式,解决了线程的安全问题;*                  操作同步代码时,只能有一个线程参与,其他线程等待,相当于是*                      一个单线程的过程,效率低;**/public class SafeThread {public static void main(String[] args) {Window window = new Window();Thread thread1 = new Thread(window);Thread thread2 = new Thread(window);Thread thread3 = new Thread(window);thread1.start();thread2.start();thread3.start();}
}
class Window implements Runnable{private int ticket = 100;
//    Object object = new Object();@Overridepublic void run() {while (true){//操作共享数据的代码,即为需要被同步的代码;//this指当前对象,调用当前方法的就是当前对象 windowsynchronized (this){//任何一个类的对象,都可以充当锁if(ticket > 0){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName() + "票号" + ticket);}else break;ticket --;}}}
}

 继承 Thread 类创建线程解决线程安全的方式;

//使用同步代码块解决继承 Thread 类的线程安全问题;//在继承 Thread 类创建多线程的方式中,慎用 this 充当同步监视器,一般使用当前类class Test2{public static void main(String[] args) {Window2 window21 = new Window2();Window2 window22 = new Window2();Window2 window23 = new Window2();window21.start();window22.start();window23.start();}
}
class Window2 extends Thread{private int ticket = 100;static Object object = new Object();//需要 static 修饰@Overridepublic void run(){while (true){//不可以用this,当前对象有window21/22/23三个对象,对象不唯一,锁不唯一synchronized(object) {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "票号:" + ticket);ticket --;}else break;}}}}
6.2 同步方法

实现 Runnable 接口创建线程的解决线程安全方式;

public class SafeThread2 {public static void main(String[] args) {Window1 window1 = new Window1();Thread thread1 = new Thread(window1);Thread thread2 = new Thread(window1);Thread thread3 = new Thread(window1);thread1.start();thread2.start();thread3.start();}
}class Window1 implements Runnable{private int ticket = 100;static Object object = new Object();@Overridepublic synchronized void  run() {while (true){show();}}private synchronized void show(){//非静态同步方法if(ticket > 0){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName()+"票号:"+ticket);ticket --;}}
}

 继承 Thread 类创建线程的解决线程安全的方式;

/*** 使用同步代码块解决 继承 Thread 类的线程安全问题:*/
class SafeWindow1{public static void main(String[] args) {Test02 t1 = new Test02();Test02 t2 = new Test02();Test02 t3 = new Test02();t1.start();t2.start();t3.start();}
}class Test02<object> extends Thread{private static int ticket = 100;@Overridepublic void run(){while (true){Show();}}private static synchronized void Show( ){//静态同步方法try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}if(ticket > 0){System.out.println(Thread.currentThread().getName()+"票号:"+ticket);ticket --;}}}
 6.3 Lock 锁

Lock(手动控制)> 同步代码块(已进入方法体,分配资源)>  同步方法(方法体制之外)

synchronized 与 lock 的异同

        相同:二者都可以解决线程安全问题

        不同:synchronized 机制在执行完相应的同步代码以后,自动释放同步监视器

                Lock 需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock)

import java.util.concurrent.locks.ReentrantLock;/*** Lock 锁,JDK5.0新增,解决线程安全问题 ----JDK5.0新增*  1.面试题:  synchronized 与 lock 的异同*      相同:二者都可以解决线程安全问题*      不同:synchronized 机制在执行完相应的同步代码以后,自动释放同步监视器*            Lock 需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())**  2.优先使用顺序:*      Lock(手动控制)>同步代码块(已进入方法体,分配资源)> 同步方法(方法体制之外)*  3. 如何解决线程安全问题?**/public class Lock {public static void main(String[] args) {Window01 window01 = new Window01();Thread thread1 = new Thread(window01);Thread thread2 = new Thread(window01);Thread thread3 = new Thread(window01);thread1.start();thread2.start();thread3.start();}
}class Window01 implements Runnable{private int ticket = 100;//1. 实例化 ReentrantLockprivate ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true){try{//没有异常不用抛//2.调用 lock()lock.lock();if(ticket > 0){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName() + "票号:" + ticket);ticket --;}else break;}finally{//3.调用解锁方法:unlock()lock.unlock();}}}
}
7. 死锁

死锁:

        1.理解: 不同的线程分别占用对方需要的同步资源不“松手”

                都在等待对方放弃自己需要的同步资源,造成一种 * 程序相持的局面;

        2.说明:

        (1)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态; *                  (2)使用同步时,要避免出现死锁

       3.解决办法:

        (1)专门的算法、原则

        (2)尽量减少同步资源的定义

        (3)尽量避免嵌套同步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/30661.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

elasticsearch是哪家的

Elasticsearch&#xff1a;数据搜索与分析的领航者 在当今这个信息爆炸的时代&#xff0c;快速且准确地处理海量数据成为了众多企业和组织追求的目标。而Elasticsearch正是在这个背景下脱颖而出的一款强大的开源搜索引擎。它是由位于美国加利福尼亚州的Elastic公司所开发和维护…

Spring学习笔记:工厂模式与反射机制实现解耦

1.什么是Spring? spring是一个开源轻量级的java开发应用框架&#xff0c;可以简化企业级应用开发 轻量级 1.轻量级(对于运行环境没有额外要求) 2.代码移植性高(不需要实现额外接口) JavaEE的解决方案 Spring更像是一种解决方案&#xff0c;对于控制层&#xff0c;它有Spring…

【一个月备战蓝桥算法】递归与递推

字典序 在刷题和计算机科学领域&#xff0c;字典序&#xff08;Lexicographical order&#xff09;也称为词典序、字典顺序、字母序&#xff0c;是一种对序列元素进行排序的方式&#xff0c;它模仿了字典中单词的排序规则。下面从不同的数据类型来详细解释字典序&#xff1a; …

前端学习——CSS

CSS CSS&#xff08;Cascading Style Sheets&#xff09;级联样式表语法 选择器全局选择器元素选择器类选择器ID选择器合并选择器选择器的优先级 字体属性字体颜色 背景属性background-color属性background-image属性background-repeat属性background-size属性background-posit…

【Python 2D绘图】Matplotlib绘图(统计图表)

【Python 2D绘图】Matplotlib绘图&#xff08;统计图表&#xff09; 1. 概述1.1 简介1.2 安装1.3 导入1.4 保存1.5 数据来源1.5.1 Numpy ndarray1.5.2 Pandas DataFrame 1.6 中文显示 2. 基础样式2.1 颜色2.1.1 简称2.1.2 全称 2.2 布局2.2.1 Matplotlib 画布划分2.2.2 绘制子图…

学习笔记:Python网络编程初探之基本概念(一)

一、网络目的 让你设备上的数据和其他设备上进行共享&#xff0c;使用网络能够把多方链接在一起&#xff0c;然后可以进行数据传递。 网络编程就是&#xff0c;让在不同的电脑上的软件能够进行数据传递&#xff0c;即进程之间的通信。 二、IP地址的作用 用来标记唯一一台电脑…

Spark-TTS:基于大模型的文本语音合成工具

GitHub&#xff1a;https://github.com/SparkAudio/Spark-TTS Spark-TTS是一个先进的文本到语音系统&#xff0c;它利用大型语言模型&#xff08;LLM&#xff09;的强大功能进行高度准确和自然的语音合成&#xff1b;旨在高效、灵活、强大地用于研究和生产用途。 一、介绍 Sp…

【RAG】检索后排序 提高回答精度

问题: RAG中&#xff0c;有时&#xff0c;最合适的答案不一定排在检索的最前面 user_query "how safe is llama 2" search_results vector_db.search(user_query, 5)for doc in search_results[documents][0]:print(doc"\n")response bot.chat(user_qu…

线程安全问题(面试重难点)

这里只是简单介绍以下线程安全,具体情况要结合代码进行判断 线程 是随机调度,及 抢占式执行 ,具有随机性,就可能会让我们的结果出现不同 当我们得到的结果并不是我们想要的时候(不符合需求),就会被认定为BUG,此时就是出现了线程安全问题 那么存在线程不安全的代码就被认为是…

数据结构第七节:红黑树(初阶)

【本节要点】 红黑树概念红黑树性质红黑树结点定义红黑树结构红黑树插入操作的分析 一、红黑树的概念与性质 1.1 红黑树的概念 红黑树 &#xff0c;是一种 二叉搜索树 &#xff0c;但 在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red和 Black 。 通过对 任何…

读书报告」网络安全防御实战--蓝军武器库

一眨眼&#xff0c;20天过去了&#xff0c;刷完了这本书「网络安全防御实战--蓝军武器库」&#xff0c;回味无穷&#xff0c;整理概览如下&#xff0c;可共同交流读书心得。在阅读本书的过程中&#xff0c;我深刻感受到网络安全防御是一个综合性、复杂性极高的领域。蓝军需要掌…

从传统到智能:Node-red工控机助力农业大棚高效监控

智慧农业逐渐成为现代农业发展的主流方向。在这一背景下&#xff0c;农业用工控机&#xff08;简称“农控机”&#xff09;作为智慧农业的核心设备之一&#xff0c;正在为农业大棚的智能化管理提供强有力的支持。本文将详细探讨农控机在智慧农业大棚监控中的应用&#xff0c;并…

硬件学习笔记--48 磁保持继电器相关基础知识介绍

目录 1.磁保持继电器工作原理 2.磁保持继电器内部结构及组成部分 3.磁保持继电器主要参数 4.总结 1.磁保持继电器工作原理 磁保持继电器利用永磁体的磁场和线圈通电产生的磁场相互作用&#xff0c;实现触点的切换。其特点在于线圈断电后&#xff0c;触点状态仍能保持&#…

WOA-Transformer鲸鱼算法优化编码器时间序列预测(Matlab实现)

WOA-Transformer鲸鱼算法优化编码器时间序列预测&#xff08;Matlab实现&#xff09; 目录 WOA-Transformer鲸鱼算法优化编码器时间序列预测&#xff08;Matlab实现&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现WOA-Transformer鲸鱼算法优化编…

K8S学习之基础十九:k8s的四层代理Service

K8S四层代理Service 四层负载均衡Service 在k8s中&#xff0c;访问pod可以通过ip端口的方式&#xff0c;但是pod是由生命 周期的&#xff0c;pod在重启的时候ip地址往往会发生变化&#xff0c;访问pod就需要新的ip地址&#xff0c;这样就会很麻烦&#xff0c;每次pod地址改变就…

R语言的基础命令及实例操作

> T & F [1] FALSE > T & T [1] TRUE > T | F [1] TRUE > F | F [1] FALSE > a <- c(T,F,T) > b <- c(F,F,T) > a & b [1] FALSE FALSE TRUE > a | b [1] TRUE FALSE TRUE 在 R 中&#xff0c;大小写是敏感的&#xff0c;也就是说…

LLM 模型 Prompt 工程

目录 1、Prompt 基础概念 2、Prompt 主要构成 3、Prompt 相关技术 3.1、思维链 3.2、自洽性 3.3、思维树 1、Prompt 基础概念 Prompt 工程是通过设计和优化自然语言提示&#xff08;Prompt&#xff09;&#xff0c;引导LLM生成符合特定任务需求的输出的技术。其核心目标是…

Springboot基础篇(4):自动配置原理

1 自动配置原理剖析 1.1 加载配置类的源码追溯 自动配置的触发入口&#xff1a; SpringBootApplication 组合注解是自动配置的起点&#xff0c;其核心包含 EnableAutoConfiguration&#xff0c;该注解使用AutoConfigurationImportSelector 实现配置类的动态加载。 启动类的注…

【大模型系列】开发工具Cursor使用配置及备忘

开发工具cursor使用过程的配置备忘 最近一段时间大模型开发工具cursor是比较火爆的&#xff0c;其提供的一个比较有价值的特性就是其ai辅助功能&#xff0c;其内部集成了若干大模型 提供免费使用期&#xff1b; 做大模型开发这个话题应该是绕不过的&#xff0c;就像开发java使…

vtkAppendPolyData vtkMultiBlockDataGroupFilter 区别 合并数据

Summary: vtkAppendPolyData vtkMultiBlockDataGroupFilter 区别 两个都是合并数据&#xff1b; 用于处理多块数据集的两种不同的过滤器&#xff08;filters&#xff09;&#xff0c;它们在处理和合并多块数据集方面有不同的用途和实现方式。 Part2:区别 它们的主要区别在于…