理解多线程看这一篇就够了

一、基本概念与关系

程序
程序是含有指令和数据的文件,静态地存储在磁盘等存储设备上。它是软件的实体,但未被激活。

进程
进程是程序的一次执行过程,是系统运行程序的基本单位。当程序被操作系统加载并执行时,就成为一个进程,具有动态性。进程拥有独立的内存空间和系统资源(如CPU时间、内存、I/O设备等),是一个正在执行中的程序实例。进程之间相互独立,不共享内存空间。

线程
线程是进程内部的一个执行单元,是进程中实际运作的小单位,也是CPU调度的基本单位。相比进程,线程更轻量,同类线程共享同一块内存空间和系统资源。线程之间可以共享数据,但也可能相互影响,特别是在同一个进程中。线程的引入提高了程序的并发执行能力。

三者之间的关系

  • 程序进程:程序通过加载执行转变为进程,实现了从静态代码到动态执行实体的转变。
  • 进程线程:一个进程可以包含多个线程,这些线程共享进程的资源,在同一进程环境下并发执行不同的任务,提高了效率和响应速度。
  • 线程相比进程,降低了资源开销,提高了通信效率,但同时也可能因为资源共享引发同步问题。

二、线程的基本状态

Java 线程在其生命周期中会经历以下6种基本状态之一,并且随着代码执行和系统调度在这些状态之间转换:

  1. 新建(New):线程刚被创建,尚未启动。
  2. 可运行(Runnable):线程可以被调度执行,包括已经获得CPU时间片正在运行的线程和等待CPU分配时间片的线程。
  3. 阻塞(Blocked):线程等待某个监视器锁(例如进入synchronized代码块时),或者等待I/O操作完成等。
  4. 等待(Waiting):线程等待其他线程执行特定操作,如调用Object.wait()方法,不可被中断直到等待条件满足。
  5. 超时等待(Timed Waiting):与等待状态相似,但线程在指定时间后会自动醒来,如调用Thread.sleep(long millis)方法。
  6. 终止(Terminated):线程已结束执行,无论是正常结束还是异常结束。

这些状态描述了线程从创建到执行完毕的完整生命周期,理解这些状态对于调试多线程程序和避免死锁等问题至关重要。
Java 线程状态如下图所示:
在这里插入图片描述

三、 线程池的原理与创建原因及方式

原理

线程池是一种基于池化思想管理线程的机制,其核心在于重用线程资源,减少创建和销毁线程的开销。线程池的工作流程主要包括以下几个步骤:

  1. 创建线程池:初始化时,预先创建一定数量的线程并将其置于待命状态,等待任务分配。
  2. 任务提交:当有新任务到达时,将其加入到任务队列中。
  3. 任务调度:线程池中的空闲线程会从任务队列中取出任务并执行。若所有线程均在忙状态,且任务队列已满,则根据策略决定是否创建新线程或拒绝任务。
  4. 任务执行完毕:线程不会立即销毁,而是返回线程池等待下一个任务。
  5. 线程管理:根据系统负载动态调整线程数量,避免过多线程导致资源耗尽或过少线程影响任务处理速度。
优点
  1. 资源重用:通过重复利用已创建的线程,减少了线程创建和销毁的开销。
  2. 提高响应速度:任务到达时无需等待新线程创建即可立即执行。
  3. 管理灵活性:可根据系统状况动态调整线程数量,有效控制资源使用,防止资源耗尽。
  4. 简化编程模型:提供统一的接口给开发者提交任务,隐藏了线程管理的复杂细节。
  5. 提高系统稳定性:通过控制最大线程数,防止了过多线程导致的资源竞争和调度开销,增强了系统的稳定性和可预测性。
创建线程池的方式
  1. 手动创建:基础方式,使用语言提供的线程创建API(如Java中的Thread类)配合同步机制(如队列、锁等)自行实现线程池逻辑。

  2. 使用标准库

    • Java: 通过Executors类提供的工厂方法创建不同类型线程池,如newFixedThreadPool创建固定大小线程池,newCachedThreadPool创建可缓存线程池等。
    • C++/POSIX: 利用std::thread结合std::queue等容器自建线程池,或使用第三方库如boost::thread_pool
    • 其他语言:如Python的concurrent.futures.ThreadPoolExecutor,Node.js的worker_threads模块等,都提供了线程池或类似机制的封装。
  3. 第三方库:使用成熟的第三方线程池库,如Java的Apache Commons ThreadPoolExecutor,C#的Microsoft TPL(Task Parallel Library)等,这些库通常提供了更高级的特性,如线程池监控、任务调度策略等。

选择合适的创建方式需根据项目需求、性能要求以及目标平台的支持程度综合考虑。

四、线程池的创建与参数解析

在Java中,通过ThreadPoolExecutor类来创建自定义线程池,其构造函数提供了高度灵活的配置选项,以便根据具体需求调整线程池的行为。下面是构造函数及其参数的详细介绍:

 
public ThreadPoolExecutor(int corePoolSize,      // 核心线程数int maximumPoolSize,   // 最大线程数long keepAliveTime,    // 空闲线程存活时间TimeUnit unit,         // 存活时间的单位BlockingQueue<Runnable> workQueue, // 任务队列RejectedExecutionHandler handler  // 拒绝策略
)
参数解释
  1. corePoolSize核心线程数
    线程池中常驻的核心线程数量。即使线程空闲,这部分线程也会保留在线程池中,不会被回收。只有在工作队列满并且当前线程数小于最大线程数时才会创建新的线程。

  2. maximumPoolSize最大线程数
    线程池能容纳的最大线程数量。当活动线程达到这个数值后,新来的任务如果不能放入任务队列将被拒绝处理。

  3. keepAliveTime空闲线程存活时间
    当线程池中的线程数量超过核心线程数时,多余的空闲线程在等待新任务最长时间达到keepAliveTime后,如果还没有新任务到来,那么这些线程将被终止以节省资源。

  4. unit存活时间的单位
    keepAliveTime参数的时间单位,如TimeUnit.SECONDS秒、TimeUnit.MILLISECONDS毫秒等。

  5. workQueue任务队列
    用于保存等待执行的任务的阻塞队列。不同的队列实现会影响线程池的行为,常见的有ArrayBlockingQueue(固定大小)、LinkedBlockingQueue(无界或有限界限)、SynchronousQueue(直接传递,没有容量)等。

  6. handler拒绝策略
    当线程池和任务队列都达到饱和状态时(即无法再接受新任务),如何处理新提交的任务。JDK内置了几种拒绝策略:

    • AbortPolicy:默认策略,丢弃任务并抛出RejectedExecutionException异常。
    • CallerRunsPolicy:调用者运行策略,直接在调用者线程中执行被拒绝的任务。
    • DiscardPolicy:静默丢弃任务,不抛出任何异常。
    • DiscardOldestPolicy:丢弃队列中最旧的任务,并尝试重新提交当前任务。

通过调整这些参数,可以创建符合特定应用场景需求的线程池,以达到资源最优利用和任务高效执行的目的。

五、 线程池原理

1. 固定大小线程池(FixedThreadPool)

特点

固定大小线程池维护一个固定数量的线程,确保所有任务都会在一个控制好的线程集合中执行,适合于负载较为稳定、任务执行时间相对均衡的场景。由于使用了无界队列LinkedBlockingQueue,如果任务提交速率超过处理能力,队列可能会无限增长,导致内存溢出风险。

参数分析:

  • corePoolSize:等于最大线程数,一旦创建就不会改变,保证线程数量恒定。

  • workQueue:默认为LinkedBlockingQueue,队列容量无上限。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class FixedThreadPoolDemo {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(3);for (int i = 0; i < 10; i++) {final int taskId = i;executor.execute(() -> {System.out.println("Task ID : " + taskId + ", Thread Name : " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}executor.shutdown();}
}
2. 可缓存线程池(CachedThreadPool)

特点:

可缓存线程池会根据任务的需求动态创建线程,空闲线程超过一定时间(默认60秒)则会被回收。这种线程池适合执行大量短期异步任务,能够迅速响应突发请求,但不适合长时间运行的任务,因为线程数量可能无限增长,导致资源耗尽。

参数分析:

  • corePoolSize=0,初始时无核心线程。
  • maximumPoolSize=Integer.MAX_VALUE,理论上允许无限增长。
  • keepAliveTime=1分钟,空闲线程等待新任务的最长时间。
  • workQueue=SynchronousQueue,直接传递,不保留任务。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CachedThreadPoolDemo {public static void main(String[] args) {ExecutorService executor = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {final int taskId = i;executor.execute(() -> {System.out.println("Task ID : " + taskId + ", Thread Name : " + Thread.currentThread().getName());});}executor.shutdown();}
}
3. 定时任务线程池(ScheduledThreadPool)

特点:

定时任务线程池用于执行定时或周期性的任务,如定时检查、定时清理等。它维护了一个固定大小的核心线程池,并使用DelayedWorkQueue作为任务队列来存放将要执行的任务。

参数分析:

  • corePoolSize:线程池的基本大小,即使没有任务执行,线程也会保持存活。
  • 支持schedule系列方法设定任务的执行时间或周期。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledThreadPoolDemo {public static void main(String[] args) {ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);for (int i = 0; i < 3; i++) {final int taskId = i;executor.scheduleAtFixedRate(() -> {System.out.println("Task ID : " + taskId + ", Thread Name : " + Thread.currentThread().getName());}, 0, 1, TimeUnit.SECONDS);}}
}

以上介绍了Java中三种常用的线程池类型,通过实例展示了它们的使用方法和特性。在实际应用中,应根据具体需求选择合适的线程池类型,并考虑是否需要进一步自定义线程池参数,以达到最佳的性能与资源利用率。尽管Executors类提供了简便的工厂方法,但在生产环境中推荐直接使用ThreadPoolExecutor构造函数来实现更细致的控制,以避免潜在的资源耗尽问题

六、常用的线程池

提交一个任务到线程池中,线程池的处理流程如下:

  • 1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创
    建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
  • 2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如
    果工作队列满了,则进入下个流程。
  • 3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
1、ThreadPoolExecutor的execute()方法
public void execute(Runnable command) {if (command == null) {throw new NullPointerException();}// 如果线程数大于等于核心线程数或者线程创建失败,将任务加入队列if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {// 线程池处于运行状态并且加入队列成功if (runState == RUNNING && workQueue.offer(command)) {if (runState != RUNNING || poolSize == 0) {ensureQueuedTaskHandled(command);}} // 线程池不处于运行状态或者加入队列失败,则创建线程(创建的是非核心线程)else if (!addIfUnderMaximumPoolSize(command)) {// 创建线程失败,则采取阻塞处理的方式reject(command); // is shutdown or saturated}}
}
2、创建线程的方法:addIfUnderCorePoolSize(command)
private boolean addIfUnderCorePoolSize(Runnable firstTask) {Thread t = null;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {if (poolSize < corePoolSize && runState == RUNNING) {t = addThread(firstTask);}} finally {mainLock.unlock();}if (t == null) {return false;}t.start();return true;
}
我们重点来看第7行:
private Thread addThread(Runnable firstTask) {Worker w = new Worker(firstTask);Thread t = threadFactory.newThread(w);if (t != null) {w.thread = t;workers.add(w);int nt = ++poolSize;if (nt > largestPoolSize) {largestPoolSize = nt;}}return t;
}

这里将线程封装成工作线程worker,并放入工作线程组里,worker类的方法run方法:

public void run() {try {Runnable task = firstTask;firstTask = null;while (task != null || (task = getTask()) != null) {runTask(task);task = null;}} finally {workerDone(this);}
}

worker在执行完任务后,还会通过getTask方法循环获取工作队里里的任务来执行。 我们通过一个程序来观察线程池的工作原理:

3、创建一个线程
public class ThreadPoolTest implements Runnable {@Overridepublic void run() {try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}}
}
4、线程池循环运行11个线程:
public static void main(String[] args) {LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(5);ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, // 核心线程数10, // 最大线程数60, // 空闲线程存活时间TimeUnit.SECONDS, // 时间单位queue // 任务队列);for (int i = 0; i < 11; i++) {threadPool.execute(new Thread(new ThreadPoolTest(), "Thread" + i));System.out.println("活跃的线程数: " + threadPool.getPoolSize());if (queue.size() > 0) {System.out.println("----------------队列阻塞的线程数" + queue.size());}}threadPool.shutdown();
}

执行结果:

活跃的线程数: 1
活跃的线程数: 2
活跃的线程数: 3
活跃的线程数: 4
活跃的线程数: 5
活跃的线程数: 5
----------------队列阻塞的线程数1
活跃的线程数: 5
----------------队列阻塞的线程数2
活跃的线程数: 5
----------------队列阻塞的线程数3
活跃的线程数: 5
----------------队列阻塞的线程数4
活跃的线程数: 5
----------------队列阻塞的线程数5
活跃的线程数: 6
----------------队列阻塞的线程数5
活跃的线程数: 7
----------------队列阻塞的线程数5
活跃的线程数: 8
----------------队列阻塞的线程数5
活跃的线程数: 9
----------------队列阻塞的线程数5
活跃的线程数: 10
----------------队列阻塞的线程数5
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task
Thread[Thread15,5,main] rejected from
java.util.concurrent.ThreadPoolExecutor@232204a1[Running, pool size = 10, active
threads = 10, queued tasks = 5, completed tasks = 0]
at
java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPool
Executor.java:2047)
at
java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at
java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at test.ThreadTest.main(ThreadTest.java:17)
分析结果

观察与分析

  1. 线程池配置概览:创建的线程池具体配置为:核心线程数量为5个;最大线程总数为10个;关联的工作队列容量为5个任务。

  2. 工作队列监控:通过queue.size()方法实时监测工作队列中的任务数量,帮助理解线程池的工作状态。

  3. 运行机制解析:

  • 初始阶段,随着任务的提交,线程池会创建核心线程直至达到5个。
  • 当核心线程满载后,新任务被加入到工作队列中,直至队列也达到其容量上限5个。
  • 队列饱和后,线程池会继续创建非核心线程,直至线程总数达到最大限制10个。
  • 若线程数和队列均达到上限,此时线程池进入饱和状态,需依据饱和策略处理后续提交的任务。

饱和策略(RejectedExecutionHandler)调整
当线程池和队列均达到饱和,即无法接纳新任务时,JDK提供了四种预设的饱和策略处理新提交的任务:

  1. AbortPolicy(默认):直接抛出RejectedExecutionException异常。
  2. CallerRunsPolicy:将任务回退给调用线程直接执行。
  3. DiscardOldestPolicy:丢弃队列中最旧的任务,尝试重新提交当前任务。
  4. DiscardPolicy:直接丢弃新提交的任务,不抛出异常。
    修改示例:现在,我们将上述示例中的饱和策略改为DiscardPolicy,即丢弃新提交的任务而不抛出异常。
public static void main(String[] args) {// ... 线程池初始化代码保持不变 ...// 设置饱和策略为DiscardPolicythreadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 任务提交逻辑保持不变 ...threadPool.shutdown();
}

基于之前的讨论,让我们整合并扩展这部分内容,包括修改线程池的饱和策略为DiscardPolicy并进行相应排版:

观察与分析

  1. 线程池配置概览:创建的线程池具体配置为:核心线程数量为5个;最大线程总数为10个;关联的工作队列容量为5个任务。

  2. 工作队列监控:通过queue.size()方法实时监测工作队列中的任务数量,帮助理解线程池的工作状态。

  3. 运行机制解析

    • 初始阶段,随着任务的提交,线程池会创建核心线程直至达到5个。
    • 当核心线程满载后,新任务被加入到工作队列中,直至队列也达到其容量上限5个。
    • 队列饱和后,线程池会继续创建非核心线程,直至线程总数达到最大限制10个。
    • 若线程数和队列均达到上限,此时线程池进入饱和状态,需依据饱和策略处理后续提交的任务。

饱和策略(RejectedExecutionHandler)调整

当线程池和队列均达到饱和,即无法接纳新任务时,JDK提供了四种预设的饱和策略处理新提交的任务:

  1. AbortPolicy(默认):直接抛出RejectedExecutionException异常。
  2. CallerRunsPolicy:将任务回退给调用线程直接执行。
  3. DiscardOldestPolicy:丢弃队列中最旧的任务,尝试重新提交当前任务。
  4. DiscardPolicy:直接丢弃新提交的任务,不抛出异常。

修改示例:现在,我们将上述示例中的饱和策略改为DiscardPolicy,即丢弃新提交的任务而不抛出异常。

public static void main(String[] args) {// ... 线程池初始化代码保持不变 ...// 设置饱和策略为DiscardPolicythreadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 任务提交逻辑保持不变 ...threadPool.shutdown();
}

通过这样的调整,当线程池和队列达到饱和状态后,任何新提交的任务将被默默地丢弃,这种方式适用于那些可以安全丢弃的任务场景,避免了因任务拒绝而引发的异常中断,提高了程序的健壮性。

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

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

相关文章

Nacos 进阶篇---Nacos服务下线做了哪些事情 ?(八)

一、引言 本章节是第一阶段最后一篇&#xff0c;那么我们今天要学习的源码内容是 “服务下线”. 当Nacos客户端下线的时候&#xff0c;是要去通知服务端&#xff0c;告诉服务端 “ 我已经下线&#xff0c;不可用了 ”。并且在服务下线时&#xff0c;还要去通知其他客户端服务更…

Linux命令那么多,先来一篇文件和目录管理命令!

&#x1f4a1;本文建议大家收藏&#xff01; 文件和目录管理命令 1. ls - 列出目录内容 ls命令是Linux中最常用的命令之一&#xff0c;用于列出目录中的文件和子目录。 ls显示当前目录下的所有文件和目录。 ls -l以长格式列出目录内容&#xff0c;显示文件权限、所有者、大…

如何便捷申请免费SSL证书,实现网站HTTPS安全传输

申请免费SSL证书的教程可以概括为以下几个通用步骤&#xff0c;这里以Lets Encrypt为例&#xff0c;因为它是最受欢迎的免费SSL证书提供商之一&#xff0c;同时也适用于多数其他免费SSL证书提供商&#xff1a; 1.准备工作 确认域名&#xff1a;确保你拥有一个有效的域名&…

CSS学习笔记:响应式布局的原理——媒体查询

什么是响应式布局&#xff1f; 在实际书写代码时&#xff0c;我们不会自己去手写媒体查询来实现响应式布局&#xff0c;我们一般会调用现成的代码库或使用现成的框架&#xff08;但这些代码库或框架的底层原理是媒体查询&#xff0c;所以了解媒体查询也是很有必要的&#xff0…

AB实验人群定向HTE模型1 - Causal Tree

背景 论文给出基于决策树估计实验对不同用户的不同影响。并提出Honest&#xff0c;variance Penalty算法旨在改进CART在tree growth过程中的过拟合问题。 我们举个例子&#xff1a;科研人员想衡量一种新的降血压药对病人的效果&#xff0c;发现服药的患者有些血压降低但有些血…

I2C协议详解

文章目录 概念工作模式 原理工作原理工作流程IIC协议的关键特点IIC通信过程 优点与缺点优点缺点 概念 IIC&#xff08;Inter-Integrated Circuit&#xff09;协议&#xff0c;也常被称为TWI&#xff08;Two-Wire Interface&#xff09;协议&#xff0c;是一种用于短距离通信的…

list常用接口模拟实现

文章目录 一、模拟list类的框架二、函数接口实现1、迭代器接口2、常用删除、插入接口3、常用其他的一些函数接口4、默认成员函数 一、模拟list类的框架 1、使用带哨兵的双向链表实现。 2、链表结点&#xff1a; // List的结点类 template<class T> struct ListNode {Li…

卧式混料机:混合设备的智慧之选

卧式混料机&#xff0c;顾名思义&#xff0c;是一种采用卧式结构的混合设备。它的设计精巧&#xff0c;结构紧凑&#xff0c;不仅占用空间小&#xff0c;而且操作简便&#xff0c;维护方便。与传统的立式混料机相比&#xff0c;卧式混料机在混合效率、混合均匀度以及物料适应性…

DNS设置(linux)

1.配置dns需要现在/etc/sysconfig/network-scripts/目录下的ifcfg-ens33(后面数字也可能是其他的)中配置DNS 2.编辑/etc/resolv.conf文件&#xff0c;将上面网卡中加的dns服务器ip添加到此文件 vi /etc/resolv.conf重启网络配置 service network restart常用的dns的ip 国内…

香港优才计划申请时间要多久?各流程申请周期规划,再晚就来不及了!

香港优才计划申请时间要多久&#xff1f;各流程申请周期规划&#xff0c;再晚就来不及了&#xff01; 2024年是香港优才计划不限配额的最后一年&#xff0c;明年政策如何变化还未可知&#xff0c;但如果明年又设置限额了&#xff0c;那么今年最后的机会一定要抓住了。 在这里…

分享 - 树形dp

树形 d p dp dp 例1 - 基础 链接&#xff1a;树上子链 练手 分析 其实一看题就很显然的树形 d p dp dp子链在这里分为两种情况&#xff0c;如图黑链和红链 思路 d p [ i ] dp[i] dp[i] 表示以 i i i 开头的红链的最大权值易得&#xff1a; d p [ i ] m a x ( d p [ i…

祝贺!阿里云PolarDB斩获数据库国际顶会ICDE 2024工业赛道最佳论文

5月17日消息&#xff0c;在荷兰举行的国际顶级数据库学术会议ICDE 2024上&#xff0c;阿里云斩获工业和应用赛道的“最佳论文奖”&#xff0c;这也是中国企业首次获此殊荣。阿里云PolarDB创新性地解决了数据库Serverless中跨机事务迁移的核心难题&#xff0c;将跨机迁移时间压缩…

智能客服:论小红书商家杀出重围的正确姿势!

小红书「起飞」密码 洞悉需求&#xff0c;主动应变 面对众多的互联网平台&#xff0c;选择一个合适的平台宣传自家的品牌&#xff0c;也是一门学问&#xff0c;从“遇事不决&#xff0c;小红书”&#xff0c;这一 slogan 就能精准地捕捉了用户搜索行为的新趋势。 在过去的十…

【C++奇妙冒险】拷贝构造函数、运算符重载(赋值重载|const成员|取地址重载|const取地址重载)

文章目录 前言&#x1f6a9;拷贝构造函数&#x1fae7;概念&#x1fae7;特征&#x1fae7;默认生成的拷贝构造&#x1fae7;default关键字&#xff08;浅谈&#xff09; &#x1f6a9;运算符重载&#x1fae7;概念&#x1fae7;运算符重载注意事项&#x1fae7;封装如何保证&a…

如何使用GPT-4o?如何使用 GPT-4o API?

如何使用GPT-4o&#xff1f; GPT-4o 也可以通过 ChatGPT 界面使用 如何使用 GPT-4o API 新的 GPT-4o 模型遵循 OpenAI 现有的聊天完成 API&#xff0c;使其向后兼容且易于使用。 ​ 如何升级GPT4Plus&#xff1f; 升级ChatGPTPLSU4需要一张虚拟卡&#xff0c;点击获取​​​…

Java(六)——抽象类与接口

文章目录 抽象类和接口抽象类抽象类的概念抽象类的语法抽象类的特性抽象类的意义 接口接口的概念接口的语法接口的特性接口的使用实现多个接口接口与多态接口间的继承抽象类和接口的区别 抽象类和接口 抽象类 抽象类的概念 Java使用类实例化对象来描述现实生活中的实体&…

【第一节】从C语言到C++

目录 一、面向对象编程 1.早期概念 2.发展与普及 3. 现代发展 二、从C语言到C 1.关于堆内存的使用 2.关于函数重载 3.关于默认参数 4.引用 5.引用参数 6.作用域符号 三、C的输入输出机制 一、面向对象编程 面向对象编程&#xff08;Object-Oriented Programming&am…

如何进一步缩短Python性能

1、问题背景 给定一个(x,y)处的节点网格&#xff0c;每个节点有一个值(0…255)从0开始。有N个输入坐标&#xff0c;每个坐标在(0…x, 0…y)的范围内。一个值Z&#xff0c;它定义了节点的“邻域”。增加输入坐标及其邻居节点的值。网格边缘之外的邻居被忽略。基准案例&#xff…

产教协同|暴雨AI算力赋能传媒实践教学

近日&#xff0c;“第七届传媒实践教学创新研讨会暨中国高校影视学会实验教学专业委员会年会”在美丽的山城重庆成功举办。本次大会以“拥抱AI&#xff0c;融合共生”为主题&#xff0c;吸引了来自全国200多所高校、30多家企业以及700多位大视听实践教学产学研用各界专家、学者…

Data Lakehouse:你的下一个数据仓库

作者&#xff1a;张友东 StarRocks TSC member/镜舟科技 CTO 数据分析是现代企业和组织决策过程中不可或缺的一部分&#xff0c;数据分析技术经过数十年的发展&#xff0c;需求场景从 BI 报表到数据探寻、实时预测、用户画像等不断丰富&#xff0c;技术架构经历从数据仓库、数据…