理解多线程看这一篇就够了
一、基本概念与关系
程序
程序是含有指令和数据的文件,静态地存储在磁盘等存储设备上。它是软件的实体,但未被激活。
进程
进程是程序的一次执行过程,是系统运行程序的基本单位。当程序被操作系统加载并执行时,就成为一个进程,具有动态性。进程拥有独立的内存空间和系统资源(如CPU时间、内存、I/O设备等),是一个正在执行中的程序实例。进程之间相互独立,不共享内存空间。
线程
线程是进程内部的一个执行单元,是进程中实际运作的小单位,也是CPU调度的基本单位。相比进程,线程更轻量,同类线程共享同一块内存空间和系统资源。线程之间可以共享数据,但也可能相互影响,特别是在同一个进程中。线程的引入提高了程序的并发执行能力。
三者之间的关系
- 程序到进程:程序通过加载执行转变为进程,实现了从静态代码到动态执行实体的转变。
- 进程到线程:一个进程可以包含多个线程,这些线程共享进程的资源,在同一进程环境下并发执行不同的任务,提高了效率和响应速度。
- 线程相比进程,降低了资源开销,提高了通信效率,但同时也可能因为资源共享引发同步问题。
二、线程的基本状态
Java 线程在其生命周期中会经历以下6种基本状态之一,并且随着代码执行和系统调度在这些状态之间转换:
- 新建(New):线程刚被创建,尚未启动。
- 可运行(Runnable):线程可以被调度执行,包括已经获得CPU时间片正在运行的线程和等待CPU分配时间片的线程。
- 阻塞(Blocked):线程等待某个监视器锁(例如进入synchronized代码块时),或者等待I/O操作完成等。
- 等待(Waiting):线程等待其他线程执行特定操作,如调用
Object.wait()
方法,不可被中断直到等待条件满足。 - 超时等待(Timed Waiting):与等待状态相似,但线程在指定时间后会自动醒来,如调用
Thread.sleep(long millis)
方法。 - 终止(Terminated):线程已结束执行,无论是正常结束还是异常结束。
这些状态描述了线程从创建到执行完毕的完整生命周期,理解这些状态对于调试多线程程序和避免死锁等问题至关重要。
Java 线程状态如下图所示:
三、 线程池的原理与创建原因及方式
原理
线程池是一种基于池化思想管理线程的机制,其核心在于重用线程资源,减少创建和销毁线程的开销。线程池的工作流程主要包括以下几个步骤:
- 创建线程池:初始化时,预先创建一定数量的线程并将其置于待命状态,等待任务分配。
- 任务提交:当有新任务到达时,将其加入到任务队列中。
- 任务调度:线程池中的空闲线程会从任务队列中取出任务并执行。若所有线程均在忙状态,且任务队列已满,则根据策略决定是否创建新线程或拒绝任务。
- 任务执行完毕:线程不会立即销毁,而是返回线程池等待下一个任务。
- 线程管理:根据系统负载动态调整线程数量,避免过多线程导致资源耗尽或过少线程影响任务处理速度。
优点
- 资源重用:通过重复利用已创建的线程,减少了线程创建和销毁的开销。
- 提高响应速度:任务到达时无需等待新线程创建即可立即执行。
- 管理灵活性:可根据系统状况动态调整线程数量,有效控制资源使用,防止资源耗尽。
- 简化编程模型:提供统一的接口给开发者提交任务,隐藏了线程管理的复杂细节。
- 提高系统稳定性:通过控制最大线程数,防止了过多线程导致的资源竞争和调度开销,增强了系统的稳定性和可预测性。
创建线程池的方式
-
手动创建:基础方式,使用语言提供的线程创建API(如Java中的
Thread
类)配合同步机制(如队列、锁等)自行实现线程池逻辑。 -
使用标准库:
- Java: 通过
Executors
类提供的工厂方法创建不同类型线程池,如newFixedThreadPool
创建固定大小线程池,newCachedThreadPool
创建可缓存线程池等。 - C++/POSIX: 利用
std::thread
结合std::queue
等容器自建线程池,或使用第三方库如boost::thread_pool
。 - 其他语言:如Python的
concurrent.futures.ThreadPoolExecutor
,Node.js的worker_threads
模块等,都提供了线程池或类似机制的封装。
- Java: 通过
-
第三方库:使用成熟的第三方线程池库,如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 // 拒绝策略
)
参数解释
-
corePoolSize:核心线程数
线程池中常驻的核心线程数量。即使线程空闲,这部分线程也会保留在线程池中,不会被回收。只有在工作队列满并且当前线程数小于最大线程数时才会创建新的线程。 -
maximumPoolSize:最大线程数
线程池能容纳的最大线程数量。当活动线程达到这个数值后,新来的任务如果不能放入任务队列将被拒绝处理。 -
keepAliveTime:空闲线程存活时间
当线程池中的线程数量超过核心线程数时,多余的空闲线程在等待新任务最长时间达到keepAliveTime后,如果还没有新任务到来,那么这些线程将被终止以节省资源。 -
unit:存活时间的单位
keepAliveTime参数的时间单位,如TimeUnit.SECONDS
秒、TimeUnit.MILLISECONDS
毫秒等。 -
workQueue:任务队列
用于保存等待执行的任务的阻塞队列。不同的队列实现会影响线程池的行为,常见的有ArrayBlockingQueue
(固定大小)、LinkedBlockingQueue
(无界或有限界限)、SynchronousQueue
(直接传递,没有容量)等。 -
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)
分析结果
观察与分析
-
线程池配置概览:创建的线程池具体配置为:核心线程数量为5个;最大线程总数为10个;关联的工作队列容量为5个任务。
-
工作队列监控:通过queue.size()方法实时监测工作队列中的任务数量,帮助理解线程池的工作状态。
-
运行机制解析:
- 初始阶段,随着任务的提交,线程池会创建核心线程直至达到5个。
- 当核心线程满载后,新任务被加入到工作队列中,直至队列也达到其容量上限5个。
- 队列饱和后,线程池会继续创建非核心线程,直至线程总数达到最大限制10个。
- 若线程数和队列均达到上限,此时线程池进入饱和状态,需依据饱和策略处理后续提交的任务。
饱和策略(RejectedExecutionHandler)调整
当线程池和队列均达到饱和,即无法接纳新任务时,JDK提供了四种预设的饱和策略处理新提交的任务:
- AbortPolicy(默认):直接抛出RejectedExecutionException异常。
- CallerRunsPolicy:将任务回退给调用线程直接执行。
- DiscardOldestPolicy:丢弃队列中最旧的任务,尝试重新提交当前任务。
- DiscardPolicy:直接丢弃新提交的任务,不抛出异常。
修改示例:现在,我们将上述示例中的饱和策略改为DiscardPolicy,即丢弃新提交的任务而不抛出异常。
public static void main(String[] args) {// ... 线程池初始化代码保持不变 ...// 设置饱和策略为DiscardPolicythreadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 任务提交逻辑保持不变 ...threadPool.shutdown();
}
基于之前的讨论,让我们整合并扩展这部分内容,包括修改线程池的饱和策略为DiscardPolicy
并进行相应排版:
观察与分析
-
线程池配置概览:创建的线程池具体配置为:核心线程数量为5个;最大线程总数为10个;关联的工作队列容量为5个任务。
-
工作队列监控:通过
queue.size()
方法实时监测工作队列中的任务数量,帮助理解线程池的工作状态。 -
运行机制解析:
- 初始阶段,随着任务的提交,线程池会创建核心线程直至达到5个。
- 当核心线程满载后,新任务被加入到工作队列中,直至队列也达到其容量上限5个。
- 队列饱和后,线程池会继续创建非核心线程,直至线程总数达到最大限制10个。
- 若线程数和队列均达到上限,此时线程池进入饱和状态,需依据饱和策略处理后续提交的任务。
饱和策略(RejectedExecutionHandler
)调整
当线程池和队列均达到饱和,即无法接纳新任务时,JDK提供了四种预设的饱和策略处理新提交的任务:
- AbortPolicy(默认):直接抛出
RejectedExecutionException
异常。 - CallerRunsPolicy:将任务回退给调用线程直接执行。
- DiscardOldestPolicy:丢弃队列中最旧的任务,尝试重新提交当前任务。
- DiscardPolicy:直接丢弃新提交的任务,不抛出异常。
修改示例:现在,我们将上述示例中的饱和策略改为DiscardPolicy
,即丢弃新提交的任务而不抛出异常。
public static void main(String[] args) {// ... 线程池初始化代码保持不变 ...// 设置饱和策略为DiscardPolicythreadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 任务提交逻辑保持不变 ...threadPool.shutdown();
}
通过这样的调整,当线程池和队列达到饱和状态后,任何新提交的任务将被默默地丢弃,这种方式适用于那些可以安全丢弃的任务场景,避免了因任务拒绝而引发的异常中断,提高了程序的健壮性。
相关文章:

理解多线程看这一篇就够了
一、基本概念与关系 程序 程序是含有指令和数据的文件,静态地存储在磁盘等存储设备上。它是软件的实体,但未被激活。 进程 进程是程序的一次执行过程,是系统运行程序的基本单位。当程序被操作系统加载并执行时,就成为一个进程&a…...
解释“this”的工作原理,原型继承如何工作,以及如何实现手写JS继承。还包括Array对象自带的方法列举,以及如何使用闭包。
1:"this"的工作原理: this 关键字指向当前执行上下文的对象,也就是当前函数被调用时所在的对象。this 的值取决于函数的调用方式,不同的调用方式会导致 this 指向不同的对象:作为对象的方法调用,this 指向该对象作为普通函数调用,this 指向全局对象(浏览器中是 wind…...

汇智知了堂实力展示:四川农业大学Python爬虫实训圆满结束
近日,汇智知了堂在四川农业大学举办的为期五天的校内综合项目实训活动已圆满结束。本次实训聚焦Python爬虫技术,旨在提升学生的编程能力和数据分析能力,为学生未来的职业发展打下坚实的基础。 作为一家在IT教育行业享有盛誉的机构ÿ…...
2024下半年软考报名人数较去年减少,仅52.77万
2024下半年软考报名人数 2024年上半年软考考试共计报考52.77万人,其中,初级资格5.12万人、中级资格24.37万人、高级资格23.28万人。 根据往年报名人数,本次考试人数是减少了的,原因分析如下: 1、原来报名热门专业系…...

【前端常见面试题整理】
开放性的题目 自我介绍 突出学习能力 我想换工作的主要原因是 介绍项目 平时是如何学习前端开发的 主要就是两个途径,一个是查阅官方文档,然后就是在网上查找技术资料或者视频去学习。平时没事的时候也会看看github,同时关注一些社区和IT网…...

Java final关键字
可以修饰类、属性、方法和局部变量。 何时使用: 1、不希望某个类被继承,用final修饰该类。 2、不希望父类的某个方法被子类覆盖/重写,用final修饰该方法。 3、不希望类的某个属性的值被修改,用final修饰该属性。 4、不希望某…...

半个月获邀请函|在读博士公派新加坡南洋理工大学联合培养
J同学计划先申报CSC联培博士,如若获批,再走本校的联培资助项目。我们仅用半个月时间,就为其申请到新加坡南洋理工大学,因导师接收名额有限制,其又热心推荐了另一位指导导师,最终J同学如愿获得学校资助出国联…...
c++移动构造和赋值的样例
#include <iostream>class MyResource { public:// 默认构造函数MyResource(size_t size 0) : m_size(size), m_data(size ? new int[size] : nullptr) {std::cout << "Default constructor called\n";}// 析构函数~MyResource() {delete[] m_data;std…...

静态测试---基于WorkList的活跃变量分析
本文主要用于记录在活跃变量分析实验中的报错及解决,涉及静态测试的详细原理内容较少,编译运行底层逻辑偏多。 一、实验要求 1)使用llvm基于框架实现一个基于WorkList的活跃变量分析demo。变量在某个程序点有两种状态,live 或 dea…...

Oracle 证书的重要性
随着信息技术的飞速发展,数据库管理已成为企业运营中不可或缺的一部分。Oracle作为全球领先的数据库管理系统提供商,其Oracle Certified Professional(OCP)认证已成为数据库管理员和开发人员追求的专业认证之一。本文将深入探讨Or…...

【Go专家编程——并发控制——Mutex】
1.Mutex 互斥锁是并发程序中对共享资源进行访问控制的主要手段,对此Go语言提供了Mutex,对外暴露Lock()和Unlock两个方法,分别用于加锁和解锁。 1.1 Mutex的数据结构 源码如下: type Mutex struct{state int32//代表互斥锁的状…...

SRE视角下的DevOps构建之道
引言: 随着数字化时代的飞速发展,软件成为了企业竞争力的核心。为了更高效地交付高质量的软件,DevOps(Development和Operations的组合)作为一种文化、实践和工具集的集合,逐渐成为了行业内的热门话题。然而…...
小白如何如何理解滑动窗口最大值问题python
文章目录 题目描述思路什么时候弹出元素什么时候加入元素 代码示例和解释 题目描述 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 举例: 输…...

Linux--进程间通信(2)(有名管道)
目录 1.原理 2.创建命名管道 3.使用命名通道实现简单的通信 4.使用创建的命名管道 1.原理 匿名管道没有名称,它们是通过句柄在父进程和子进程之间传递的。这意味着匿名管道只能用于具有父子关系的进程之间。 但如果程序之间没关系,那么这时候就要用…...

window自动启动bat文件
开机自动开启远程桌面, WinR 执行netplwiz 命令进入设置;取消勾选,可选择所需用户,点击应用,输入远程的密码即可 开机自动开启远程桌面, WinR 执行netplwiz 命令进入设置;取消勾选࿰…...

2024年蓝桥杯Web开发【大赛大纲】15届
一、 组别 Web应用开发分为:大学组和职业院校组。 每位选手只能申请参加其中一个组别的竞赛。各个组别单独评奖。 研究生和本科生只能报大学组。 其它高职高专院校可自行选择报任意组别。 二. 竞赛赛程 省赛时长:4小时。 决赛时长:4小…...

【vue-cli搭建vue项目的过程2.x】
vue-cli搭建vue项目 vue-cli搭建vue项目安装node安装vue-cli脚手架并创建项目安装 Ant Design Vue或element-ui(笔者使用Ant-design-vue组件,并全局引入)开发安装三方库包1、Package.json文件---引入如下package.json文件执行npm i或npm install命令即可下载如下依赖…...

Android 生成正式版密钥库 KeyStore
步骤1:打开生成正式版密钥库设置 点击 Build 菜单,选择 Generate Signed App Bundle or APK: 这是打开后的样子: 步骤2:选择 APK Android App Bundle 是用于上架 Google Play 商店的。 正常情况下选择 APK。 选择…...

POLARDB:新零售用户MySQL上云最佳选择
什么是云数据库POLARDB? POLARDB是阿里云自主研发的最新一代RDS关系型数据库,是特别针对互联网场景设计的Cloud-Native 云原生数据库。POLARDB for MySQL版本,在提供100%兼容MySQL5.6/8.0的关系型事务处理ACID特性之上,能够提供完…...

PHP MySQL图解学习指南:开启Web开发新篇章
PHP曾经是最流行的Web开发语言,许多世界领先的网站(如Facebook、维基百科和WordPress)都是用它编写的。PHP运行在Web服务器端,通过使用存储在MySQL数据库中的数据,使得网站可以为每一位访问者显示不同的定制页面。书中采用简单、直观的图示化…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...

手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...