java学习--多线程
多线程

了解多线程
多线程是指从软件或者硬件上实现多个线程并发执行的技术。
具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能。
并发和并行
- 并行:在同一时刻,有多个指令在CPU上同时执行
- 并发:在同一时刻,有多个指令在CPU上交替执行
进程和线程
进程:正在运行的软件
- 独立性:进程是一个独立运行的基本单位,同时也是系统分配调度资源的独立单位。
- 动态性:进程的实质就是程序的一次执行过程,动态产生,动态消亡。
- 并发性:任何进程都可以和其他进程并发执行
线程:是进程中单个顺序控制流,是一条执行路径
- 单线程:一个进程如果只有一条执行路径,则称为单线程程序
- 多线程:一个进程如果有多条执行路径,则成为多线程程序
多进程的实现方案
- 继承Thread类的方式进行实现
- 定义一个类继承Thread类
- 在定义的类中重写run()方法
- 创建自定义类的对象
- 启动线程
- 实现Runable接口的方式实现
- 自定义一个类实现Runable接口
- 在自定义类中重写run()方法
- 创建自定义对象
- 创建Tread类对象,把自定义对象作为构造方法的参数
- 启动线程
- 利用Callable和Future接口方式实现
- 定义一个类实现Callable接口
- 在该类中重写call方法
- 创建自定义类的对象
- 创建Future的实现类FutureTask对象,把自定义类对象作为构造方法的参数
- 创建Tread类的对象,把FutureTask对象最为构造方法的参数
- 启动线程
三种方式的对比
| 优点 | 缺点 | |
|---|---|---|
| 实现Runnable,Callable接口 | 扩展性强,实现该接口的同时还可以继承其他类 | 编程相对复杂,不能直接使用Thread类中的方法 |
| 继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 可扩展性差,不能继承其他类 |
使用getName()获取当前正在执行现成的名称
使用setName()给当前线程设置名称,也可使用子类的带参构造方法设置名称
使用Thread的Sleep()方法 使得线程睡眠特定的时间
实现Runnable结构创建多线程程序的好处
- 避免了单继承的局限性
- 一个类只能继承一个类,自定义类继承了Thread类就不能继承其他类
- 实现了Runnable接口,还可以继承其他类,实现其它接口
- 增强了程序的扩展性,降低了程序的耦合性
- 实现了Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
- 实现类中,重写了run方法,用来设置线程任务
- 创建Thread类对象,调用start方法,用来开启新的线程
线程安全问题
解决措施:
使用同步机制解决
方法一:同步代码块
syncheonized(对象){代码块
}
方法二:同步方法
使用步骤:
- 把访问了共享数据的代码提取出来,放到一个方法中
- 在方法上添加synchronized修饰符
- 格式
修饰符 synchronized 返回值类型 方法名(参数列表){可能会出现安全问题的代码(访问了共享数据的代码)
}
线程池
线程池可以看成是一个池子,这个池子中存储很多个线程
系统创建一个线程的成本是很高的,因为他涉及到与操作系统的交互,当程序需要创建大量生存期很短暂的线程是,频繁的创建何晓辉线程对系统的资源消耗可能大于业务处理对线程的消耗。为了提高性能,我们可以采用线程池。
线程池在启动时,会创建大量空闲线程,当我们向线程池提价搜任务是,线程池就会启动一个线程来执行该任务。等待任务执行完毕,线程并不会死亡,而是咋次返回到线程池中称为空闲状态,等待哦下一次任务的执行。
线程池的设计思路
- 准备一个任务容器
- 一次性启动多个消费者线程
- 刚开始任务容器是空的,所有线程都在等待
- 知道一个外部线程向这个任务容器扔了一个“任务”,就会有一个消费者线程被唤醒
- 这个消费者线程取出任务,并执行任务,执行完毕后,继续等待下一次任务的到来
线程池-Executors默认线程池
概述:在开发中我们使用JDK中自带的线程池
我们可以使用Excutors中所提供的的静态方法来创建线程池
static ExcutorsService newCachedThreadPool()创建一个默认线程池
static newFixedThreadPool(int nThreads)创建一个指定最多线程数量的线程池
代码实现
ackage practise2.Exam2;import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;//static ExecutorsService new CachedThreadPool() 创建一个默认的线程池
//static newFixedThreadsPool(int nThreads) 创建一个指定最多线程数量的线程池
public class Exam1 {public static void main(String[] args) {//创建一个默认线程池对象,池子是空的,默认最多可以容纳int类型的最大值ExecutorService executorService= Executors.newCachedThreadPool();//Executors ---可以帮助我们创建线程池对象//ExecutorService ---可以帮助我们控制线程池executorService.submit(()->{System.out.println(Thread.currentThread().getName()+"在执行了");});executorService.submit(()->{System.out.println(Thread.currentThread().getName()+"在执行了");});executorService.shutdown();}
}
线程池-Executors创建指定上限的线程池
使用Executors中所提供的静态方法来创建线程池
static ExecutorsService newFixcedThreadPool(int nThread):创建一个指定最多线程数量的线程池
代码实现:
package practise2.Exam2;import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;//static ExecutorService newFixedThreadPool(int nThread):创建一个指定最多线程数量的线程池
public class Exam2 {public static void main(String[] args) {//参数不是初始值而是最大值ExecutorService executorService= Executors.newFixedThreadPool(10);ThreadPoolExecutor pool= (ThreadPoolExecutor) executorService;System.out.println(pool.getPoolSize()); //0executorService.submit(()->{System.out.println(Thread.currentThread().getName()+"在执行了");});executorService.submit(()->{System.out.println(Thread.currentThread().getName()+"在执行了");});System.out.println(pool.getPoolSize());//2//executorService.shutdown();}
}
线程池-ThreadPoolExecutor
创建线程池对象:
ThreadPoolExecutors threadPoolExecutor =new ThreadPoolExecutor(核心线程数连发,最大线程数量,空闲线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
代码实现
package practise2.Exam2;import pracise1.exam5.MyRunnable;import java.util.concurrent.*;public class Exam3 {public static void main(String[] args) {//参数一:核心线程数量//参数二:最大线程数//参数三:空闲线程最大存活时间//参数四:时间单位//参数五:任务队列//参数六:创建线程工厂//参数七:人物的拒绝策略ThreadPoolExecutor pool =new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());pool.submit(new MyRunnable());pool.submit(new MyRunnable());pool.shutdown();}
}
线程池-参数详解
创建线程池对象
ThreadPoolExecutor threadPoolExecutor =new ThreadPoolExecutor(
核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略
)
| 参数 | 含义 | 限制 |
|---|---|---|
| 参数一 | 核心线程数量 | 不能小于0 |
| 参数二 | 最大线程数 | 不能小于等于0,最大线程数大于等于核心线程数 |
| 参数三 | 空闲线程最大存活空间 | 不能小于0 |
| 参数四 | 时间单位 | 时间单位 |
| 参数五 | 任务队列 | 不能为null |
| 参数六 | 创建线程工厂 | 不能为null |
| 参数七 | 人物的拒绝策略 | 不能为null |
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
corePoolSize: 核心线程的最大值,不能小于0
maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
keepAliveTime: 空闲线程最大存活时间,不能小于0
unit: 时间单位
workQueue: 任务队列,不能为null
threadFactory: 创建线程工厂,不能为null
handler: 任务的拒绝策略,不能为null
线程池-非默认任务拒绝策略
RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,他下面存在4个子类。
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中。
ThreadPoolExecutor。CallerRunsPolicy: 调佣任务的run()方法绕过线程池直接执行
注:明确线程池对多可执行的任务数=队列容量+最大线程数
package practise2.Exam2;import java.util.concurrent.*;public class Exam {public static void main(String[] args) {
// ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,3,20,
// TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),
// Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());//提交5个任务,而该线程池最多可以处理4个任务,当我们使用ABortPOlicy这个任务处理策略是后,就会抛出异常ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,3,20,TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());//提交了5个任务for (int i = 0; i < 5; i++) {//定义一个变量,来制定当前执行的任务,这个变量需要被final修饰final int y=i;threadPoolExecutor.submit(()->{//System.out.println(Thread.currentThread().getName()+"------>>执行了任务");System.out.println(Thread.currentThread().getName()+"---->>执行了任务"+y);});}}
}
package practise2.Exam2;import java.util.concurrent.*;public class Exam5 {public static void main(String[] args) {ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,3,20, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());//提交五个任务for (int i = 0; i < 5; i++) {threadPoolExecutor.submit(()->{System.out.println(Thread.currentThread().getName()+"---->>执行了任务");});}}
}
通过控制台的输出,我们可以看次策略没有通过线程池中得到线程执行任务,而是直接调用任务的run()方法绕过线程池直接执行。
原子性
volatile-问题
代码分析:
volatile解决
以上案例出现的问题:
当A线程修改了共享数据时,B线程没有及时获取道最新的值,如果还在使用原先的值,就会出现问题。
- 堆内存是唯一的,每一个线程都有自己的线程栈
- 每一个线程在使用堆内存里面的变量时,都会先拷贝一份到变量的副本中
- 在线程中,每一次使用是从变量的副本中获取的
volatile关键字:强制线程在每一次使用时,都会看一下公共区域最新的值。
public class Money {public static volatile int money=100000;
}
public class MyThread1 extends Thread{@Overridepublic void run() {while (Money.money==100000){}System.out.println("结婚基金已经不是十万了");}
}
public class MyThread2 extends Thread{@Overridepublic void run() {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}Money.money=90000;}
}
public class Demo {public static void main(String[] args) {MyThread1 myThread1=new MyThread1();myThread1.setName("小垃圾");myThread1.start();MyThread2 myThread2=new MyThread2();myThread2.setName("小趴菜");myThread2.start();}}
synchronized解决
- 线程获得锁
- 清空变量副本
- 拷贝共享最新的值到变量副本中
- 执行代码
- 将修改后变量副本中的值赋值给共享数据
- 释放锁
代码实现
原子性
概述:在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可分割的整体。
Volatile关键字不能保证原子性
解决方案:我们可以给count++操作添加锁,那么count++操作就是临界区中的代码,临界区中的代码一次只能被一个线程去执行,所以count++就变成了的原子操作。
原子性—AtomicInteger
概述:java从JDK1.5开始提供了java.uyil.concurrent.atomic包(简称Atomic包),这个包中的原子提供了一种用法简单,性能高效,线程安全地根新一个变量的方式,因为变量的类型有很多个,所以在Atomic包中一共提供了13个类,属于4种类型的原子更新方式,分别是:
原子更新基本类型,原子更新数组,原子更新引用和原子更行属性(字段)
使用原子的方式甘心基本类型,使用原子的方式更新基本类型Atomic包提供了一下3个类:
AtomicBoolean:原子更新布尔类型
AtomicInteger:原子更新整型
AtomicLong:原子更新长整型
以上三个类提供的方法几乎一模一样,以AtomicInteger为例讲解
public AtomicInteger(); //初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue): //初始化一个指定值的原子型Integerint get(); //获取值
int getAndIncrement(); //以原子的方式将当前值加1,注意,这里返回的是自增前的值
int incrementAndGet(); //以原子的方式将当前值加1,注意,这里返回的是自增前的值
int addAndGet(int data): //以原子的方式将输入的数值与示例中的值(AtomicInteger里的value)相加,并返回结果。
int getandSet(int value): //以原子方式设置为newValur的值,并返回旧值
代码实现:
AtomicInteger-内存解析
AtomicInteger原理:
自旋锁+CAS算法
CAS算法:
有三个操作数(内存值V,旧的预期值A,要修改的值B)
当旧的预期值A==内存值 此时修改成功,将V改为B
当旧的预期值A!=内存值,此时修改失败,不做任何操作
并重新获取现在的最新值(这个重新获取的动作就是自旋)
AtomicInteger-源码解析
代码实现:
源码解析
乐观锁和悲观锁
synchronized和CAS的区别
相同点:
在多线程的情况下,都可以保证共享数据的安全性
不同点:
synchronized总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每次操作共享数据之前,都会上锁。(悲观锁)
cas是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁,只不过在修改共享数据的时候,会检查一下。
如果别人修改过,则获取最新数据。
如果别人没有修改过,那么我们直接修改共享数据的值(乐观锁)
并发工具类
并发 工具类-Hashtable
Hashtable出现的原因:
在集合类中HashMap是比较常用的集合类对象,但是HashMap是现成不安全的(多线程环境下可能会存在问题)。为了保证数据得到安全性我们可以使用Hashtable,但是Hashtable的效率低下。
代码实现:
并发工具类-ConcurrentHashMap基本使用
ConcurrentHashMap出现的原因
在集合类中HashMap是比较常用的集合对象,但是hashMap是线程不安全的多线程环境下可能会存在的问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下,基于以上两个原因我们可以使用JDK1.5以后提供的ConcurrentHashMap。
体系结构
ConcurrentHashMap
Map接口
- HashMap
- Hashtable
- TreeMap
- ConcurrentMap
总结:
- HashMap是线程不安全的,多线程环境下有数据安全问题
- hashtable 是线程安全的,但是会将整张表锁起来,效率低下‘
- ConcurrentHashMap也是线程安全的,效率高,在JDK7和JDK8中,底层原理不同
相关文章:
java学习--多线程
多线程 了解多线程 多线程是指从软件或者硬件上实现多个线程并发执行的技术。 具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能。 并发和并行 并行:在同一时刻,有多个指令在CPU上同时执行并发࿱…...
90后阿里P7技术专家晒出工资单:狠补了这个,真香...
最近一哥们跟我聊天装逼,说他最近从阿里跳槽了,我问他跳出来拿了多少?哥们表示很得意,说跳槽到新公司一个月后发了工资,月入5万多,表示很满足!这样的高薪资着实让人羡慕,我猜这是税后…...
2023美赛C题:Wordle筛选算法
Wordle 规则介绍 Wordle 每天会更新一个5个字母的单词,在6次尝试中猜出单词就算成功。每个猜测必须是一个有效的单词(不能是不能组成单词的字母排列)。 每次猜测后,字母块的颜色会改变,颜色含义如下: 程…...
SpringBoot 集成 Kafka
SpringBoot 集成 Kafka1 安装 Kafka2 创建 Topic3 Java 创建 Topic4 SpringBoot 项目4.1 pom.xml4.2 application.yml4.3 KafkaApplication.java4.4 CustomizePartitioner.java4.5 KafkaInitialConfig.java4.6 SendMessageController.java5 测试1 安装 Kafka Docker 安装 Kafk…...
OpenCV 图像金字塔算子
本文是OpenCV图像视觉入门之路的第14篇文章,本文详细的介绍了图像金字塔算子的各种操作,例如:高斯金字塔算子 、拉普拉斯金字塔算子等操作。 高斯金字塔中的较高级别(低分辨率)是通过先用高斯核对图像进行卷积再删除偶…...
【自学Linux】Linux一切皆文件
Linux一切皆文件 Linux一切皆文件教程 Linux 中所有内容都是以文件的形式保存和管理的,即一切皆文件,普通文件是文件,目录是文件,硬件设备(键盘、监视器、硬盘、打印机)是文件,就连套接字&…...
CUDA C++扩展的详细描述
CUDA C扩展的详细描述 文章目录CUDA C扩展的详细描述CUDA函数执行空间说明符B.1.1 \_\_global\_\_B.1.2 \_\_device\_\_B.1.3 \_\_host\_\_B.1.4 Undefined behaviorB.1.5 __noinline__ and __forceinline__B.2 Variable Memory Space SpecifiersB.2.1 \_\_device\_\_B.2.2. \_…...
为什么重写equals必须重写hashCode
关于这个问题,看了网上很多答案,感觉都参差不齐,没有答到要点,这次就记录一下! 首先我们为什么要重写equals?这个方法是用来干嘛的? public boolean equals (Object object&#x…...
< 每日小技巧:N个很棒的 Vue 开发技巧, 持续记录ing >
每日小技巧:6 个很棒的 Vue 开发技巧👉 ① Watch 妙用> watch的高级使用> 一个监听器触发多个方法> watch 监听多个变量👉 ② 自定义事件 $emit() 和 事件参数 $event👉 ③ 监听组件生命周期常规写法hook写法ὄ…...
数据结构与算法之二分查找分而治之思想
决定我们成为什么样人的,不是我们的能力,而是我们的选择。——《哈利波特与密室》二分查找是查找算法里面是很优秀的一个算法,特别是在有序的数组中,这种算法思想体现的淋漓尽致。一.题目描述及其要求请实现无重复数字的升序数组的…...
训练自己的中文word2vec(词向量)--skip-gram方法
训练自己的中文word2vec(词向量)–skip-gram方法 什么是词向量 将单词映射/嵌入(Embedding)到一个新的空间,形成词向量,以此来表示词的语义信息,在这个新的空间中,语义相同的单…...
ubuntu系统环境配置和常用软件安装
系统环境 修改文件夹名称为英文 参考链接 export LANGen_US xdg-user-dirs-gtk-update 常用软件安装 常用工具 ping 和ifconfig工具 sudo apt install -y net-tools inetutils-ping 截图软件 sudo apt install -y net-tools inetutils-ping flameshot 录屏 sudo apt-get i…...
【1139. 最大的以 1 为边界的正方形】
来源:力扣(LeetCode) 描述: 给你一个由若干 0 和 1 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0。 示例 1&#…...
windows11安装sqlserver2022报错
window11安装SQL Server 2022 报错 糟糕… 无法安装SQL Server (setup.exe)。此 SQL Server安装程序介质不支持此OS的语言,或没有SQL Server英语版本的安装文件。请使用匹配的特定语言SQL Server介质;或安装两个特定语言MUI,然后通过控制面板的区域设置…...
Python快速上手系列--日志模块--详解篇
前言本篇主要说说日志模块,在写自动化测试框架的时候我们就需要用到这个模块了,方便我们快速的定位错误,了解软件的运行情况,更加顺畅的调试程序。为什么要用到日志模块,直接print不就好了!那得写多少print…...
【THREE.JS学习(1)】绘制一个可以旋转、放缩的立方体
学习新技能,做一下笔记。在使用ThreeJS的时候,首先创建一个场景const scene new THREE.Scene();接着,创建一个相机其中,THREE.PerspectiveCamera()四个参数分别为:1.fov 相机视锥体竖直方向视野…...
数仓实战 - 滴滴出行
项目大致流程: 1、项目业务背景 1.1 目的 本案例将某出行打车的日志数据来进行数据分析,例如:我们需要统计某一天订单量是多少、预约订单与非预约订单的占比是多少、不同时段订单占比等 数据海量 – 大数据 hive比MySQL慢很多 1.2 项目架…...
python虚拟环境与环境变量
一、环境变量 1.环境变量 在命令行下,使用可执行文件,需要来到可执行文件的路径下执行 如果在任意路径下执行可执行文件,能够有响应,就需要在环境变量配置 2.设置环境变量 用户变量:当前用户登录到系统,…...
BeautifulSoup文档4-详细方法 | 用什么方法对文档树进行搜索?
4-详细方法 | 用什么方法对文档树进行搜索?1 过滤器1.1 字符串1.2 正则表达式1.3 列表1.4 True1.5 可以自定义方法2 find_all()2.1 参数原型2.2 name参数2.3 keyword 参数2.4 string 参数2.5 limit 参数2.6 recursive 参数3 find()4 find_parents()和find_parent()5…...
初识Tkinter界面设计
目录 前言 一、初识Tkinter 二、Label控件 三、Button控件 四、Entry控件 前言 本文简单介绍如何使用Python创建一个界面。 一、初识Tk...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
