多线程代码案例

案例一.单例模式

单例模式是一种设计模式;类似于棋谱,有固定套路,针对一些特定场景可以给出一些比较好的解决方案;

只要按照设计模式来写代码,就可以保证代码不会太差,保证了代码的下限;

-------------------------------------------------------------------------------------------------------------------------------

补充:

设计模式是针对编写代码过程中的软性约束: 不是强制的,可以遵守也可以不遵守;

框架是针对编写代码过程中的硬性约束: 针对一些特定的问题场景,大佬们把基本的代码和大部分逻辑已经写好了,留下一些空位,让你在空位上自定义一些逻辑;

虽然也是别人写好的,但是代码的主体还是由你来完成,你可以决定调用或者不调用;

-------------------------------------------------------------------------------------------------------------------------------

开发过程中,希望有的类在一个进程中不应该存在多个实例(对象),此时就可以使用单例模式,限制某个类只有一个实例;

饿汉模式

饿的意思是"迫切": 在类被加载的时候就会创建出单例的实例;

class Singleton {private static Singleton instance = new Singleton();//static修饰将instance变成类成员;//类成员的初始化就是在Singleton这个类被加载的时候;public static Singleton getInstance() {return instance;}
}public class demo20 {public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);}
}
//true

只要不再其他代码中,new这个类,每次需要使用时都通过getInstance()来获取实例,那么此时这个类就是单例的了;

主要要解决的问题:防止别人new这个类的对象

单例模式的核心:将构造方法设为私有的

意味着在类的外面,就无法再构造新的实例了;

private Singleton() {}

通过反射/序列化反序列化等非常规手段还是可以打破单例模式的;

懒汉模式

计算机的"懒"是褒义词: 意思是效率会更高;

推迟了创建实例的时机,第一次使用的时候,才会创建实例;

class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance() {if(instance == null) {instance = new SingletonLazy();}return instance;}private SingletonLazy() {}
}public class demo21 {public static void main(String[] args) {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();System.out.println(s1 == s2);}
}
//true

思考:如果在多线程环境下,调用getInstance,是否会有问题呢?

饿汉模式没有线程安全问题,但是懒汉模式存在线程安全问题;

原因:多个线程针对一个变量修改可能会产生线程安全问题,但是如果只是读取,则没有问题;

而饿汉模式中的getInstance方法中只有读操作,而懒汉模式中的getInstance方法则有判断和修改赋值操作,故会出现线程安全问题;

解决办法:加锁!

private static Object locker = new Object();public static SingletonLazy getInstance() {synchronized (locker) {if (instance == null) {instance = new SingletonLazy();}}return instance;}

当前代码的写法,只要调用getInstance,都会触发加锁操作,虽然没有线程安全问题了,但是Instance new出来了之后就都是读操作,此时也会因为加锁,产生阻塞,影响性能;

优化:

public static SingletonLazy getInstance() {if(instance == null) {synchronized (locker) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}

-------------------------------------------------------------------------------------------------------------------------------

补充:

如果是一个局部变量,每个线程的局部变量都有自己的一份.但是如果是new出来的对象,可以共享;

少数局部变量在线程中不能共用是java自身的限制,在C++,系统原生api中则没有这样的限制;

创建出的局部变量,处于JVM内存的"栈"区域中;

new出来的变量,处于JVM内存的"堆'区域中;

整个JVM进程中,只有一份,是线程之间大家共用的;而则是每个线程有自己独立的栈;

正因为变量的共享是常态,所以就容易触发多个线程修改同一个变量导致线程安全问题;

-------------------------------------------------------------------------------------------------------------------------------

instance = new SingletonLazy();可以分为三个步骤

1.分配内存空间;(买房)

2.执行构造方法;(装修)

3.内存空间的地址,赋值给引用变量;(收房拿到钥匙)

编译器可能按照123的顺序也可能按照132的顺序来执行;对于单线程来说是没有区别的;

在多线程中按照132的顺序执行可能会出现问题:

若按照此顺序,线程A执行到步骤3时(此时instance的地址就不为null了),线程B进行了第一个if语句的判断并返回了instance,注意此时的instance指向了一个没有初始化的空的内存,故可能会产生线程安全问题;

解决方法:给instance加上volatile关键字;

private static volatile SingletonLazy instance = null;

加上volatile之后就能防止对instance的赋值操作插入到其他操作之间;

因此java的volatile有两个功能:

(1)保证内存可见性;

(2)禁止指令重排序[针对赋值];

实例二.阻塞队列

基本概念

标准库中原有的队列Queue和其子类,默认都是线程不安全的;

阻塞队列,就是在普通队列的基础上做了扩充~

(1)是线程安全的;

(2)具有阻塞特性:

a.如果队列为空,进行出队列操作,此时就会出现阻塞;一直阻塞到其他线程往队列里添加元素为止;

b.如果队列为满,进行入队列操作,此时也会进行阻塞;一直阻塞到其他线程从队列中取出元素为止;

基于阻塞队列,最大的应用场景就是实现"生产者消费者模型";

-------------------------------------------------------------------------------------------------------------------------------

使用生产者消费者模型主要有两方面好处:

(1)服务器之间的"解耦合"(即降低模块之间的关联/影响程度);

"阻塞"队列是代码中的一种数据结构,由于太好用了以至于会被单独封装成一个服务器程序,并且在单独的服务器机器上进行部署,此时这个阻塞队列就有了一个新的名字:"消息队列" (Message Queue, MQ); 

让服务器A通过阻塞队列来和服务器B进行交互,此时虽然服务器A和服务器B有与阻塞队列有了耦合,但是服务器A,B与阻塞队列交互的代码几乎不会更改故可以忽略不计; 

(2)通过中间的阻塞队列,可以起到"削峰弱谷"的效果:在遇到请求量激增的情况下,可以有效的保护服务器不会被请求冲垮;

假设原来服务器A的写入速度略小于和服务器B的处理速度,当服务器A往阻塞队列中写入速度徒增时,服务器B可以依然按照原有的速度来进行处理,此时阻塞队列起到一个缓冲的作用;

如果是直接调用A收到多少请求B也会收到多少请求,很容易把服务器B给搞崩;

问:a.为什么服务器收到的请求越多,就可能会挂?

服务器每次收到一个请求,处理这个请求的过程就需要执行一系列代码,在执行这些代码的过程中,就会消耗一定量的硬件资源(CPU,内存,硬盘,网络带宽......);当这些请求消耗的总的硬件资源超过了机器能提供的上限,那么此时机器就会出现问题(卡死,程序直接崩溃等等);

b.在请求激增的时候,为什么是B最容易挂?

A的角色是一个"网关服务器",收到客户端的请求,在把请求转发给其他服务器.这样的服务器中的代码,做的工作比较简单,消耗的硬件资源也会更少;同理,阻塞队列也是比较简单的程序,单位请求消耗的硬件资源,也是比较少的;而B这个服务器是真正干活的服务器,要真正完成一系列的业务逻辑,这一系列的工作,代码量非常的庞大,消耗的硬件资源和时间更多;

生产者消费者模型的代价:

1.需要更多的机器,来部署这样的消息队列.(易解决)

2.A与B之间通信的延迟会变长;若对性能要求很高则不太适合;

-------------------------------------------------------------------------------------------------------------------------------

阻塞队列在Java标准库中也提供了现成的封装:BlockingQueue;

offer,poll这些Queue中的方法在BlockingQueue中也有实现;

但是BlockingQueue还有两个专属的方法:put入队列,take出队列;最大的差别在于put和take是可以阻塞的;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;public class demo22 {public static void main(String[] args) throws InterruptedException {BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);//生产者线程Thread t1 = new Thread(() -> {int i = 1;while(true) {try {queue.put(i);System.out.println("生产元素" + i);i++;Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});//消费者线程Thread t2 = new Thread(() -> {while(true) {try {Integer i = queue.take();System.out.println("消费元素" + i);} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();t2.start();}
}
/*
消费元素1
生产元素1
生产元素2
消费元素2
生产元素3
消费元素3
生产元素4
消费元素4
生产元素5
消费元素5
生产元素6
消费元素6
......
*/

实际开发中,一般是多个线程生产多个线程消费;

MyBlockingQueue实现

拓展:

//1
head++;
if(head >= this.data.length) {head = 0;
}
//2
head++;
head = head % this.data.length;

此时应该优先选择第一种写法,理由如下:

(1)写法1的代码可读性更高;

(2)写法1的效率更高;对第二种写法,CPU计算乘除法是一个比较慢的操作(除非是对2的N次方进行乘除),尤其是除法;对第一种写法是判定,if判断往往是一个非常简单快速的cmp指令;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;class MyBlockingQueue {private String[] data = null;private volatile int head = 0;private volatile int tail = 0;private volatile int size = 0;
//避免内存可见性问题,即使加锁后效率低概率小但是还是要预防public MyBlockingQueue(int capacity) {this.data = new String[capacity];}public void put(String s) throws InterruptedException {synchronized (this) {while (size == this.data.length) {//return;this.wait();}data[tail] = s;tail++;while (tail >= data.length) {tail = 0;}size++;this.notify();}}public String take() throws InterruptedException {String ret = "";synchronized (this) {while (size == 0) {//return null;this.wait();}ret = data[head];head++;while (head >= data.length) {head = 0;}size--;this.notify();}return ret;}
}public class demo23 {public static void main(String[] args) throws InterruptedException {MyBlockingQueue queue = new MyBlockingQueue(10);//生产者线程Thread t1 = new Thread(() -> {int i = 1;while(true) {try {queue.put("" + i);System.out.println("生产元素" + i);i++;Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});//消费者线程Thread t2 = new Thread(() -> {while(true) {try {Integer i = Integer.parseInt(queue.take());System.out.println("消费元素" + i);} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();t2.start();}
}

注意:wait()最好被包含在while循环中而不是if判断语句;

原因:wait被唤醒的途径不止notify一种,可能也会由于其他原因唤醒,比如interrupt;

若wait被包含在try-catch语句中,且catch语句中并未结束线程:则使用if语句则会让程序继续往下走,即使队列中还是空着的,也依然会执行下面的代码,此时就会出现bug;故应该将wait语句包含在while的条件循环中;

实例三.线程池

Java标准库中,也提供了现成的线程池(ThreadPoolExecutor)供我们使用;

在java.util.concurrent包中(简称juc);

"池'这种思想,本质上就是能提高线程的效率;

随着业务上对性能的要求越来越高,对应的线程创建/销毁也变得比较频繁,此时的开销就不能忽略不计了;

线程池就是解决上述问题的常见方案,就是把线程提前从系统中申请好,放到一个地方,后面需要使用线程的时候,就直接到这个地方来取,而不是重新从系统申请;线程用完了之后,也是还回到刚才这个地方;

为什么更高效呢?

---------------------------------------------------------------------------------------------------------------------------------

内核态 & 用户态

是操作系统中的概念; 操作系统 = 操作系统内核(核心功能部分,负责完成一个操作系统的核心工作) + 操作系统配套的应用程序;

应用程序都是由内核统一负责管理和服务,内核的工作可能非常繁忙,提交给内核做的任务可能是不可控的;

从系统创建线程,就相当于让银行的人让我复印,这样的逻辑就是调用系统的api,由系统内核执行一系列逻辑来完成这个过程;

直接从线程池里取,就相当于是自助复印,整个过程都是纯用户态代码,都是咱们自己控制的,整个过程更可控高效;

用户态更加高效.

---------------------------------------------------------------------------------------------------------------------------------

java库中线程池的构造方法:

1.int corePoolSize:核心线程数 && int maxinumPoolSize:最大线程数;

说明此线程池可以进行"线程扩容";

在Java标准库中的线程池中,就把里面的线程分为两类:

(1)核心线程[可以理解为最少要有多少个线程];

会始终存在于线程池的内部;

(2)非核心线程[线程扩容的过程中,新增的线程];

繁忙的时候被创建出来,不繁忙了空闲了,就会把这些线程真正的释放掉;

核心线程数和非核心线程数的最大值就叫最大线程数;

2.long keepAliveTime:非核心线程允许摸鱼的最大时间, TimeUnit unit:是一种枚举类型,表示时间的单位;

非核心线程会在线程空闲的时候被销毁;

3.BlockingQueue<Runnable> workQueue:工作队列;

线程池工作的过程就是典型的"生产者消费者模型";

程序员使用的时候,通过形如"submit"这样的方法,把要执行的任务,设定到线程池里;

线程池内部的工作线程,负责执行这些任务;

此处的阻塞队列可以让我们自行指定:

(1)队列的容量 capacity;

(2)队列的类型;

"Runnable"接口本身的含义就是一段可以执行的任务;

4.ThreadFactory threadFactory:线程工厂;

工厂指的是"工厂设计模式"也是一种常见的设计模式;

工厂设计模式,是一种在创建实例时使用的设计模式;

由于构造方法是有"坑"的,通过工厂设计模式来填坑:

构造方法是一种特殊的方法,必须和类名是一样的; 多个版本的构造方法,必须是通过"重载"来实现的;

比如一个类描述一个平面直角坐标系中的一个点,可以用横坐标和纵坐标进行表示,也可以用极坐标进行表示,可此时要调用的构造方法所需传入的参数都是两个double类型,传入的参数个数和类型都相同,无法实现;

为了解决上述问题,就引入了"工厂设计模式",通过"普通方法"(一般是静态方法)来完成对象的构造和初始化操作:

class Point {
}class PointFactory{public static Point makePointByXY(double x, double y) {Point p;p.setX(x);p.setY(y);return p;}public static Point makePointByRA(double r, double a) {Point p;p.setR(r);p.setA(r);return p;}
}

用来创建对象的static方法就叫"工厂方法",有时候工厂方法也会放到专门的类中实现,用来放工厂方法的类就叫做"工厂类";

ThreadFactory就是Thread类的工厂类.通过这个类,完成Thread的实例创建和初始化;

5.RejectedExecutionHandler handler拒绝策略!!!

如果线程池的任务队列满了,还是要继续给这个队列添加任务,咋办呢??

当队列满了,不要阻塞,而是要明确的拒绝;

Java标准库给出了四种不同的拒绝策略:

(1)AbortPolicy

添加任务的时候,直接抛出异常(RejectedExecutionEception);

(2)CallerRunsPolicy

线程池拒绝执行,但是由调用submit的线程负责执行;

(3)DiscardOldestPolicy

把任务队列中最老的队列踢掉,然后执行添加新的任务;

(4)DiscardPolicy

把任务队列中,最新的任务踢掉;

---------------------------------------------------------------------------------------------------------------------------------

ThreadPoolExcutor功能很强大,但是使用起来很麻烦;

标准库对这个类进一步的封装了一下:Excutors提供了一些工厂方法,可以更方便的构造出线程池;

newCachedThreadPool();

设置了非常大的最大线程数:就可以对线程池进行不断的扩容;

newFixedThreadPool();

把核心线程数和最大线程数设定成了一样的值:固定数量的线程,不会扩容;

newSingleThreadPool();

newScheduledThreadPool();

newWorkStealingPool();

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class demo25 {public static void main(String[] args) throws InterruptedException {ExecutorService service = Executors.newFixedThreadPool(4);for(int i = 0; i < 100; i++) {int id = i;service.submit(() -> {Thread current = Thread.currentThread();System.out.println("hello Thread" + id + ',' + current.getName());});}Thread.sleep(1000);//不能直接打印i,lambda表达式中或者匿名类中使用的外部变量要么是常量,要么是未经修改的;//此处线程池创建出来的线程都是前台线程,虽然main线程结束了,//但是这些线程结束了,但是这些线程池的前台线程依然存在;service.shutdown();//把所有的线程都终止掉;System.out.println("程序退出") ;}
}
hello Thread0,pool-1-thread-1
hello Thread4,pool-1-thread-1
hello Thread5,pool-1-thread-1
hello Thread6,pool-1-thread-1
hello Thread7,pool-1-thread-1
hello Thread8,pool-1-thread-1
......
程序退出

线程池需要指定线程个数,多少才是合适的呢?

不能套公式:

(1)一台主机上并不是只运行一个程序;

(2)程序并不会100%跑满CPU,线程工作的过程中,可能会涉及到一些IO操作/阻塞操作主动放弃CPU[sleep,wait,加锁,打印,网络通信,读写硬盘...];

实际开发中,建议通过实验的方式来找到一个合适的线程数量;

---------------------------------------------------------------------------------------------------------------------------------

自己实现一部分功能:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;class MyThreadPool {private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);public MyThreadPool(int n) {//n表示要创建几个线程for(int i =0; i < n; i++) {Thread t = new Thread(()-> {while(true) {try {Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}}public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}
}public class demo26 {public static void main(String[] args) throws InterruptedException {MyThreadPool pool = new MyThreadPool(4);for(int i = 0; i < 100; i++) {int id = i;pool.submit(() -> {System.out.println("task" + id + "Thread:" + Thread.currentThread().getName());});}}
}

实例四.定时器

定时器相当于一个闹钟:

代码中也经常需要设定"闹钟"机制;网络通信中,经常需要设定一个"等待时间".

定时器要实现的任务:

1.创建类,描述一个要执行的任务是啥;(任务的内容,任务的时间)

2.要管理多个任务;

通过一定的数据结构,把多个任务存起来.

3.有专门的线程,执行这里的任务;

Java标准库中也实现了定时器的实现:

(若定时器时间相同,有的定时器是串行的,有的定时器是并发的)

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

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

相关文章

【优选算法】(第三十六篇)

目录 ⼆叉树的锯⻮形层序遍历&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 ⼆叉树的最⼤宽度&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 ⼆叉树的锯⻮形层序遍历&#xff08;medium&#xff09; 题目解析 1.题目链接&#xf…

植物大战僵尸杂交版

最新版植物大战僵尸杂交版 最近本款游戏火爆 下载资源如下&#xff1a; win版本&#xff1a;2.3.7 链接&#xff1a;下载地址 提取码&#xff1a;9N3P Mac&#xff08;苹果版本&#xff09;&#xff1a;2.0.0 链接&#xff1a;下载地址 提取码&#xff1a;Bjaa 介绍&#xff…

mysql/doris 计算两个时间相差n天n时n分示范

mysql/doris 计算两个时间相差n天n时n分示范 两个时间&#xff1a;so.create_time&#xff0c;so.update_time CONCAT(FLOOR(DATEDIFF(HOUR ,so.create_time,so.update_time)/24),天,DATEDIFF(HOUR ,so.create_time,so.update_time)%24,时,DATEDIFF(MINUTE ,so.create_time,so…

【重学 MySQL】六十六、外键约束的使用

【重学 MySQL】六十六、外键约束的使用 外键约束的概念关键字主表和从表/父表和子表外键约束的创建条件外键约束的特点外键约束的创建方式外键约束的删除外键约束的约束等级外键约束的级联操作外键约束的示例外键约束的作用开发场景阿里开发规范 在MySQL中&#xff0c;外键约束…

(已解决)vscode使用launch.json进行debug调试报错:Couldn‘t spawn debuggee:embedded null byte

Launch.json 进行debug时报错&#xff1a; 主要原因是vscode全局配置被整乱了&#xff0c;下面是个人解决的方法&#xff0c;以供参考. 在网上也寻找过解决方法&#xff0c;有的说是&#xff0c;在launch.json中&#xff0c;添加一行"python":"/root/miniconda3…

git版本控制软件,操作方法

git版本库操作 1. 注册用户信息 git config --global (邮箱和用户名) 2. 创建工作区 git init 3. 编写文件 vim readme.txt 4. 把文件放到暂存区 git add readme.txt 5. 查看工作区状态 git status 6. 把文件放到本地版本库里 git commit -m "" filename 7. 查看日志…

总结拓展十四:批次管理(2)

1、批次管理后台配置 1.1 批次管理级别配置(T-code:OMTC) ——路径&#xff1a;IMG->后勤-常规->批次管理->指定级别并激活状态管理 1.2 批次状态管理配置(T-code:OMTC) ——路径&#xff1a;IMG->后勤-常规->批次管理->指定级别并激活状态管理 批状态管…

2.1.ReactOS系统NtReadFile函数的实现。

ReactOS系统NtReadFile函数的实现。 ReactOS系统NtReadFile函数的实现。 文章目录 ReactOS系统NtReadFile函数的实现。NtReadFile函数的定义NtReadFile函数的实现 NtReadFile()是windows的一个系统调用&#xff0c;内核中有一个叫NtReadFile的函数 NtReadFile函数的定义 NTS…

【Go初阶】两万字快速入门Go语言

初见golang语法 package mainimport "fmt"func main() {/* 简单的程序 万能的hello world */fmt.Println("Hello Go")} 第一行代码package main定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包&#xff0c;如&#xff1a;package main…

如何捕捉行情爆发的前兆

在金融市场的激烈角逐中&#xff0c;每一次行情的爆发都是投资者获取丰厚回报的关键时刻。然而&#xff0c;如何识别并把握这些时刻&#xff0c;却是一门需要深厚金融专业知识和敏锐洞察力的艺术。今天&#xff0c;我们就来深入探讨行情爆发的初期信号&#xff0c;揭示那些能够…

【Linux】嵌入式Linux系统的组成、u-boot编译

Linux—嵌入式Linux系统的组成、u-boot编译 前言一、嵌入式Linux系统的组成1.1 嵌入式Linux系统和PC完整的操作系统的对比如下&#xff1a;1.2 PC机—Windows系统启动流程&#xff08;PC机—Linux系统、嵌入式ARM—linux系统的启动流程类似&#xff09; 二、编译u-boot2.1 u-bo…

【数据分享】我国第七次人口普查的100m分辨率人口栅格数据(免费获取\tif格式\2020年)

人口空间分布数据是我们在各项研究中经常使用的数据。之前我们分享过来源于LandScan数据集的2000-2022年的1km精度的人口空间分布栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01; 相较于LandScan全球人口数据集&#xff0c;我国历次人口普查的数据对于…

【node】初识node

前言 目标 1 为什么要学习node 2 node如何安装 #mermaid-svg-KR8iFyZTmb86RU67 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KR8iFyZTmb86RU67 .error-icon{fill:#552222;}#mermaid-svg-KR8iFyZTmb86RU67 .error…

QT--QPushButton设置文本和图标、使能禁能、信号演示

按钮除了可以设置显示文本之外&#xff0c;还可以设置图标 文本 可以获取和设置按钮上显示的文本 // 获取和设置按钮的文本 QString text() const void setText(const QString &text)该属性&#xff0c;既可以在 Qt 设计师右侧的属性窗口中修改&#xff0c;也可以在代码…

OpenAI的Swarm是一个实验性质的多智能体编排框架

先上文档&#xff0c;然后解释&#xff0c;然后是代码 OpenAI的Swarm是一个实验性质的多智能体编排框架&#xff0c;旨在简化多智能体系统的构建、编排和部署。以下是对Swarm的详细介绍&#xff1a; 一、核心概念和特点 智能体&#xff08;Agent&#xff09;&#xff1a; Swar…

int QSqlQuery::size() const

返回结果的大小&#xff08;返回的行数&#xff09; 或者返回-1 &#xff08;如果大小不能被决定 或者 数据库不支持报告查询的大小信息&#xff09; 注意&#xff1a;对于非查询语句&#xff0c;将返回-1&#xff08;isSelect()返回false&#xff09; 如果查询不是活跃的&…

支付宝开放平台-开发者社区——AI 日报「10 月 15 日」

1 10年后手机有多科幻&#xff1f;清华孙茂松&#xff1a;人手一个超级大脑&#xff0c;诊病翻译搞研发 新智元&#xff5c;阅读原文 我们有办法将大模型「化大为小」&#xff0c;同时其智能能力没有太多下降&#xff0c;从而以一种「小而美」的方式达至生成式人工智能与手机…

Linux下内核空间和用户空间内存映射图详解

目录 一、简介二、内存空间定义三、内存权限四、内存空间映射图4.1 32位系统4.2 64位系统4.3 映射空间解析 五、其他相关链接1、关于linux下内存管理内容总结2、Linux内核中kzalloc分配内存时用的参数GFP_KERNEL详解3、Linux下stream内存带宽测试参数和示例详解附源码总结 一、…

简易STL实现 | Map 的实现

提供了键值对的存储机制&#xff0c;处理 具有唯一键的关联数据 1、特性 键值对存储&#xff1a;std::map 通过键值对的形式 存储数据&#xff0c;其中每个键 都是唯一的&#xff0c;并且 与一个值相关联 自动排序&#xff1a;std::map 内部 使用一种平衡二叉搜索树&#xf…

uniapp 实现input聚焦时选中内容(已封装)兼容微信小程序

老规矩先来看看效果噻&#xff01; 从上面的录屏中我们可以看出&#xff0c;要实现input自由选中内容的功能是可以实现的&#xff0c;原理其实很简单。 直接运行即可 <template><view><!-- <input class"psd"type"digit" :value"in…