Android第十一次面试多线程篇
面试官:
“你在项目里用过Handler吗?能说说它是怎么工作的吗?”
候选人:
“当然用过!比如之前做下载功能时,需要在后台线程下载文件,然后在主线程更新进度条。这时候就得用Handler来切回主线程。简单来说,Handler就像个快递员,负责把子线程的任务包裹(比如更新UI的指令)送到主线程去执行。”
面试官追问:
“比喻挺有意思。那这个‘快递员’是怎么把消息从子线程送到主线程的?”
候选人:
“其实核心是靠三个东西:Handler
、Looper
和MessageQueue
。比如主线程启动时,系统会默认创建一个Looper
,它内部维护了一个消息队列(MessageQueue
)。当我在子线程通过Handler
发送消息,这个消息会被放到主线程的队列里。然后主线程的Looper
会循环检查队列,一有消息就取出来,交给对应的Handler
处理。”
面试官:
“那如果我在子线程里直接new一个Handler,会有什么问题吗?”
候选人:
“这里有个坑!如果子线程没有提前准备Looper
,直接new Handler会崩溃。比如这样——”
new Thread(() -> {// ❌ 错误写法:子线程默认没有LooperHandler handler = new Handler();
}).start();
“正确的做法是先调用Looper.prepare()
创建Looper,再启动循环:”
new Thread(() -> {Looper.prepare(); // ✅ 初始化LooperHandler handler = new Handler();Looper.loop(); // 启动消息循环
}).start();
面试官:
“提到主线程的Looper,你知道它是怎么初始化的吗?”
候选人:
“这个我之前研究过源码!主线程的Looper
是在ActivityThread
的main()
方法里初始化的。大概流程是:”
- 启动App:系统调用
ActivityThread.main()
。 - 准备Looper:调用
Looper.prepareMainLooper()
,创建主线程的Looper和消息队列。 - 开启循环:调用
Looper.loop()
,让主线程进入无限循环,不断处理消息(比如点击事件、UI更新)。
“所以主线程的Handler能一直运行,全靠这个死循环撑着。”
面试官:
“实际开发中有没有遇到过Handler导致的问题?比如内存泄漏。”
候选人:
“遇到过!比如在Activity里声明一个非静态内部类的Handler,如果Activity关闭时还有未处理的消息,Handler会持有Activity的引用,导致内存泄漏。我们项目里用了一个经典解法——”
// ✅ 正确写法:静态内部类 + 弱引用
static class SafeHandler extends Handler {private WeakReference<Activity> mActivity;SafeHandler(Activity activity) {mActivity = new WeakReference<>(activity);}@Overridepublic void handleMessage(Message msg) {Activity activity = mActivity.get();if (activity == null) return;// 处理消息...}
}
“另外,在Activity的onDestroy()
里,还要调用handler.removeCallbacksAndMessages(null)
清空所有消息。”
面试官:
“假设现在有个需求:每隔1秒更新一次UI上的计时器。用Handler怎么实现?”
候选人:
“可以用postDelayed()
递归调用。比如这样——”
private int count = 0;
private Handler handler = new Handler();private void startTimer() {handler.postDelayed(new Runnable() {@Overridepublic void run() {textView.setText(String.valueOf(++count));handler.postDelayed(this, 1000); // 递归调用}}, 1000);
}// 停止时调用
private void stopTimer() {handler.removeCallbacksAndMessages(null);
}
“不过要注意及时移除回调,否则退出页面时可能还在后台跑。”
面试官:
“如果不用Handler,还有其他方式实现线程间通信吗?”
候选人:
“当然!比如用AsyncTask
(虽然过时了)、LiveData
+协程,或者RxJava
的线程切换。比如协程可以这样写——”
// 在ViewModel里
fun startTask() {viewModelScope.launch(Dispatchers.IO) {val data = fetchData() // 后台执行withContext(Dispatchers.Main) {updateUI(data) // 切回主线程}}
}
“不过Handler的优势是更底层,适合需要精细控制消息队列的场景,比如实现定时任务或延迟操作。”
面试官:
“最后一个问题:为什么主线程的Looper不会导致ANR?”
候选人:
“这是个好问题!虽然Looper.loop()
是死循环,但主线程大部分时间处于休眠状态,通过Linux的epoll
机制监听消息队列。当没有消息时,线程会释放CPU进入休眠;有新消息(比如点击事件、屏幕刷新信号)时,线程被唤醒处理消息。所以只要不在主线程做耗时操作,循环本身不会阻塞,也就不会ANR。”
面试官:
“回答得很清晰。你还有什么问题想问吗?”
候选人:
“咱们项目中有没有特别依赖Handler的场景?比如自定义消息协议或者复杂定时任务?”
面试官:
(假设回答)
“有的!比如IM模块的消息重发机制,用Handler管理消息的延迟重试;还有首页的轮播图动画,用Handler的postDelayed
实现自动切换。之后你可以参与这部分优化。”
Handler 的工作流程与底层原理
1. 核心组件及其关系
- Handler:消息的发送者和处理者。
- Looper:消息循环的核心,每个线程有且只有一个。
- MessageQueue:消息队列,按时间排序存储消息(单链表实现)。
- Message:携带数据和目标Handler的单元。
关系图:
Thread├── Looper│ └── MessageQueue└── Handler(关联到Looper)
2. 工作流程
步骤1:初始化Looper(子线程)
- 子线程中必须手动调用
Looper.prepare()
创建Looper。 Looper.loop()
启动消息循环。
new Thread(() -> {Looper.prepare(); // 初始化Looper(内部创建MessageQueue)Handler handler = new Handler(Looper.myLooper());Looper.loop(); // 开始循环处理消息
}).start();
步骤2:发送消息
- 通过
Handler.sendMessage()
或Handler.post(Runnable)
发送消息。
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "Data";
handler.sendMessage(msg); // 消息入队到MessageQueue
步骤3:消息处理
- Looper从MessageQueue取出消息,调用目标Handler的
dispatchMessage()
。 - 最终触发
handleMessage()
或Runnable.run()
。
public void handleMessage(Message msg) {switch (msg.what) {case 1: updateUI((String) msg.obj); break;}
}
3. 底层原理
消息存储(MessageQueue)
- 数据结构:单链表按
when
(执行时间)排序。 - 入队操作:
enqueueMessage()
添加消息到链表合适位置。 - 出队操作:
next()
取出下一条消息(可能阻塞)。
消息循环(Looper.loop())
- 无限循环:持续调用
MessageQueue.next()
。 - 阻塞唤醒机制:
- 队列为空时,通过
epoll
或pipe
进入休眠。 - 新消息入队时唤醒线程(通过
nativeWake()
)。
- 队列为空时,通过
线程隔离(ThreadLocal)
- Looper存储:每个线程的Looper实例通过
ThreadLocal<Looper>
保存。 - 获取方式:
Looper.myLooper()
返回当前线程的Looper。
4. 主线程的Looper
- 默认初始化:ActivityThread的
main()
方法调用Looper.prepareMainLooper()
。 - 永不退出:主线程的Looper循环保证应用持续响应事件。
// ActivityThread.java
public static void main(String[] args) {Looper.prepareMainLooper(); Looper.loop(); // 主线程进入无限循环
}
5. 性能优化与注意事项
- 避免阻塞主线程:耗时操作仍要放在子线程。
- 内存泄漏:Handler持有外部类(如Activity)引用时,需用弱引用。
static class SafeHandler extends Handler {private WeakReference<Activity> mActivity;SafeHandler(Activity activity) {mActivity = new WeakReference<>(activity);}public void handleMessage(Message msg) {Activity activity = mActivity.get();if (activity == null) return;// 处理消息}
}
6. 代码示例:子线程与主线程通信
// 主线程创建Handler
Handler mainHandler = new Handler(Looper.getMainLooper());// 子线程执行任务后更新UI
new Thread(() -> {String result = doBackgroundWork();mainHandler.post(() -> textView.setText(result)); // 切换到主线程
}).start();
Kotlin协程详解:原理、优势与应用场景
一、协程的核心概念
协程(Coroutine) 是一种轻量级的并发设计模式,允许以同步编码风格实现异步任务。它通过挂起(Suspend)和恢复(Resume)机制管理任务,避免传统多线程开发的复杂性。
二、协程为何优于线程?
对比维度 | 协程 | 线程 |
---|---|---|
创建开销 | 极低(约几十字节) | 高(约1MB内存 + 系统调用开销) |
切换成本 | 无需操作系统介入(用户态切换) | 需内核介入(上下文切换开销大) |
并发数量 | 单线程可运行数万协程 | 受限于系统资源(通常数百个) |
开发复杂度 | 同步代码风格,逻辑清晰 | 需处理锁、回调、线程同步等问题 |
资源利用率 | 挂起时释放线程,避免阻塞 | 线程阻塞时资源浪费 |
三、协程的工作原理
1. 挂起与恢复
- 挂起函数(Suspend Function):
使用suspend
关键字标记的函数,可在不阻塞线程的情况下暂停执行(如等待网络响应)。suspend fun fetchData(): String {delay(1000) // 模拟耗时操作,非阻塞return "Data loaded" }
- 底层机制:
协程通过状态机和Continuation实现挂起恢复。编译器将挂起函数转换为带有回调的状态机代码。
2. 协程调度器(Dispatchers)
- 调度线程池:
launch(Dispatchers.IO) { /* 执行IO操作 */ } launch(Dispatchers.Main) { /* 更新UI */ }
- 调度策略:
- IO:适用于网络、文件操作(线程池优化)
- Default:CPU密集型任务(如排序、计算)
- Main:UI线程操作(Android主线程)
3. 结构化并发
- 协程作用域(CoroutineScope):
管理协程生命周期(如viewModelScope
、lifecycleScope
),确保任务不会泄漏。viewModelScope.launch {val data = fetchData() // 自动绑定ViewModel生命周期updateUI(data) }
四、协程如何避免回调地狱?
1. 回调地狱示例
// 传统嵌套回调
api.getUser { user ->api.getProfile(user) { profile ->api.getFriends(profile) { friends ->updateUI(friends) // 嵌套层级深,难维护}}
}
2. 协程解决方案
viewModelScope.launch {try {val user = api.getUser() // 挂起,不阻塞线程val profile = api.getProfile(user)val friends = api.getFriends(profile)updateUI(friends) // 顺序执行,逻辑清晰} catch (e: Exception) {showError(e)}
}
- 优势:
- 线性代码:消除嵌套,可读性高
- 统一异常处理:
try/catch
捕获所有异步错误
五、协程的典型应用场景
- 网络请求:
suspend fun loadData() = withContext(Dispatchers.IO) {retrofitService.fetchData() }
- 数据库操作:
fun insertUser(user: User) = viewModelScope.launch(Dispatchers.IO) {database.userDao().insert(user) }
- 并发任务组合:
val result1 = async { task1() } val result2 = async { task2() } val combined = result1.await() + result2.await() // 并行执行
- 超时与重试:
val data = withTimeoutOrNull(5000) { // 5秒超时retry(3) { // 重试3次fetchData()} }
六、协程与线程的底层协作
- 线程池复用:
协程调度器基于ExecutorService
,通过线程池复用减少开销。 - 挂起优化:
挂起时释放线程资源,交由其他协程使用,最大化利用CPU。
Kotlin协程实战
面试官:
“你在项目里用过Kotlin协程吗?能举个实际例子说说它解决了什么问题吗?”
候选人:
“当然!之前做商品详情页的时候,需要同时调三个接口:商品信息、用户评论、推荐列表。如果用传统的回调,代码会嵌套三层,像俄罗斯套娃一样,维护起来特别头疼。后来换成协程,代码直接‘拉直’了——”
viewModelScope.launch {try {// 同步写法,其实是异步执行!val product = api.fetchProductDetails() // 第一个接口val comments = api.fetchComments(product.id) // 等第一个完成后调第二个val recommendations = api.fetchRecommendations() // 等前两个都完成// 统一更新UIshowData(product, comments, recommendations)} catch (e: Exception) {// 一个try/catch抓住所有网络错误showErrorToast("加载失败,请重试")}
}
“这样一来,代码像写同步逻辑一样直观,新人也能快速看懂,而且异常处理集中,不会漏掉某个回调里的错误。”
面试官追问:
“听起来确实简洁。那协程到底是怎么做到‘假装’同步的?底层不会卡住主线程吗?”
候选人:
“这就是协程的聪明之处!比如api.fetchProductDetails()
是个挂起函数,执行到的时候,协程会悄悄挂起,把线程让出来去处理其他任务。等网络数据回来了,协程再‘醒来’,从刚才挂起的位置继续执行。整个过程主线程完全没被阻塞,用户滑屏幕照样流畅。”
面试官:
“那如果我要同时调三个接口,等所有结果一起回来再刷新界面,用协程怎么优化?”
候选人:
“这时候可以用async
并发!比如这样——”
viewModelScope.launch {// 同时发起三个请求val productDeferred = async { api.fetchProductDetails() }val commentsDeferred = async { api.fetchComments() }val recommendationsDeferred = async { api.fetchRecommendations() }// 等三个全部完成(实际耗时等于最慢的那个接口)val product = productDeferred.await()val comments = commentsDeferred.await()val recommendations = recommendationsDeferred.await()updateUI(product, comments, recommendations)
}
“比串行调用快多了!之前用回调得用计数器或者RxJava的zip
操作符,协程两行代码搞定。”
面试官:
“协程会不会导致内存泄漏?比如页面关了但请求还没回来。”
候选人:
“这就是结构化并发的优势了!比如用viewModelScope
启动协程,当ViewModel被销毁时,所有关联的协程会自动取消。如果这时候正在等网络请求,会直接中断,避免内存泄漏。我们之前有个页面没注意这个,用户快速进出会导致旧的请求继续回调,用了viewModelScope
后问题彻底解决。”
面试官:
“如果遇到老代码用回调的API,怎么接入协程?”
候选人:
“Kotlin给了逃生舱!比如有个老版SDK用回调返回数据,可以用suspendCoroutine
把它包成挂起函数——”
suspend fun legacyFetchData(): String = suspendCoroutine { continuation ->legacyApi.getData(object : Callback {override fun onSuccess(result: String) {continuation.resume(result) // 成功时恢复协程}override fun onFailure(error: Throwable) {continuation.resumeWithException(error) // 抛异常}})
}// 新代码里直接调用
viewModelScope.launch {val data = legacyFetchData() // 像普通挂起函数一样用
}
“这样旧代码也能融入协程体系,团队迁移成本低。”
面试官:
“假设你要下载10张图片,怎么用协程控制并发,同时不拖垮手机?”
候选人:
“这时候得用协程的调度器!比如这样——”
// 限制最多同时3个下载
val dispatcher = Dispatchers.IO.limitedParallelism(3)
viewModelScope.launch {val jobs = (1..10).map { index ->launch(dispatcher) { // 限制并发的协程池downloadImage(index) }}jobs.joinAll() // 等所有下载完成showToast("下载完成!")
}
“如果用普通线程池,开10个线程内存可能扛不住。协程的Dispatchers.IO
自带线程复用,加上并发限制,既高效又省资源。”
面试官:
“最后一个问题:为什么说协程适合Android开发?”
候选人:
“三个字——快、稳、省!
- 快:代码写起来像同步,维护效率高,再也不用和回调地狱搏斗。
- 稳:结构化并发自动管理生命周期,结合
viewModelScope
/lifecycleScope
,内存泄漏少一半。 - 省:一个线程跑几百个协程,用过的都说像‘开挂’,尤其低端机上效果明显。”
面试官:
“回答得很到位。你还有什么问题吗?”
候选人:
“咱们团队用协程遇到过印象深刻的坑吗?比如和LiveData、Room结合时的注意点?”
面试官:
(假设回答)
“早期在Room里混用协程和RxJava时有过线程冲突,后来统一用协程+Flow就解决了。你提到的Dispatchers.IO
限制并发,我们在大文件上传模块也用过类似优化!”
线程池实战
面试官:
“你在项目中是怎么管理多线程任务的?比如同时处理多个网络请求。”
候选人:
“我们用的是线程池。比如在APP首页要同时加载多个图片,如果每个请求都开一个新线程,手机扛不住,容易卡顿或者OOM。线程池就像个‘线程调度中心’,复用已有的线程,避免频繁创建销毁的开销。”
面试官追问:
“那具体怎么配置线程池?比如核心线程数、队列这些参数怎么定?”
候选人:
“得看任务类型。比如我们有个需求是批量上传图片,这种IO密集型任务,核心线程数可以设成CPU核心数的两倍左右,比如4核手机就设8个。队列用无界队列(比如LinkedBlockingQueue
),保证任务不丢失。但如果是CPU密集型任务,比如视频转码,核心线程数应该和CPU数差不多,避免线程切换拖慢速度。”
面试官:
“如果任务太多,线程池处理不过来了怎么办?”
候选人:
“这时候就得看拒绝策略了。比如我们遇到过用户疯狂点击触发大量请求,队列满了之后默认策略是抛异常,结果直接崩溃。后来改成了CallerRunsPolicy
,让提交任务的线程自己执行,这样至少不会崩,但得注意别阻塞主线程。”
ThreadPoolExecutor executor = new ThreadPoolExecutor(4, // 核心线程8, // 最大线程30, TimeUnit.SECONDS,new LinkedBlockingQueue<>(100), // 队列容量100new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
面试官:
“提到线程池类型,你知道Executors.newCachedThreadPool()
有什么坑吗?”
候选人:
“这个坑踩过!CachedThreadPool
的最大线程数设置的是Integer.MAX_VALUE
,如果任务无限提交,线程数暴增,直接OOM。我们之前有个日志上报功能用了它,结果用户疯狂操作时内存爆炸。后来改成自定义线程池,限制最大线程数,问题才解决。”
面试官:
“如果让你设计一个定时任务,比如每隔5秒检查一次消息,用线程池怎么实现?”
候选人:
“可以用ScheduledThreadPool
,比如这样——”
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {checkNewMessage(); // 执行任务
}, 0, 5, TimeUnit.SECONDS);
“不过要注意任务执行时间别超过间隔,否则会堆积。比如任务要跑10秒,间隔设5秒,就会变成每10秒执行一次。”
面试官:
“实际开发中,怎么确保线程池里的任务不被重复执行?”
候选人:
“我们项目里遇到过!比如用户快速点击按钮触发多次提交。解决办法是给任务加唯一ID,用ConcurrentHashMap
记录正在执行的任务,提交前先检查是否存在——”
ConcurrentHashMap<String, Boolean> taskMap = new ConcurrentHashMap<>();void submitTask(String taskId, Runnable task) {if (taskMap.putIfAbsent(taskId, true) == null) {executor.execute(() -> {try {task.run();} finally {taskMap.remove(taskId);}});}
}
面试官:
“最后一个问题:为什么推荐用ThreadPoolExecutor
而不是Executors
?”
候选人:
“Executors
的方法虽然方便,但隐藏了参数细节,容易踩坑。比如newFixedThreadPool
用的无界队列,任务堆积可能内存溢出。直接new ThreadPoolExecutor
可以明确指定核心线程数、队列容量和拒绝策略,对资源控制更精细。”
面试官:
“回答得很到位。你还有什么问题想问吗?”
候选人:
“咱们项目里线程池一般用在哪些场景?有没有特别复杂的配置案例?”
面试官:
(假设回答)
“比如首页的瀑布流图片加载用了IO密集型线程池,后台数据同步用了单线程池保证顺序。有个复杂案例是结合PriorityBlockingQueue
实现任务优先级,高优先级任务插队执行。”
Android第三次面试总结之activity和线程池篇(补充)_android线程池面试题-CSDN博客https://blog.csdn.net/2301_80329517/article/details/147700637
Android第三次面试总结(activity和线程池)_android线程池面试题-CSDN博客https://blog.csdn.net/2301_80329517/article/details/146325189?spm=1011.2415.3001.5331
相关文章:

Android第十一次面试多线程篇
面试官: “你在项目里用过Handler吗?能说说它是怎么工作的吗?” 候选人: “当然用过!比如之前做下载功能时,需要在后台线程下载文件,然后在主线程更新进度条。这时候就得用Handler来切…...

安全,稳定可靠的政企即时通讯数字化平台
在当今数字化时代,政企机构面临着复杂多变的业务需求和日益增长的沟通协作挑战。BeeWorks作为一款安全,稳定可靠的政企即时通讯数字化平台,凭借其安全可靠、功能强大的特性,为政企提供了高效、便捷的沟通协作解决方案,…...
craw4ai 抓取实时信息,与 mt4外行行情结合实时交易,基本面来觉得趋势方向,搞一个外汇交易策略
结合实时信息抓取、MT4行情数据、基本面分析的外汇交易策略框架,旨在通过多维度数据融合提升交易决策质量:行不行不知道先试试,理论是对的,只要基本面方向没错 策略名称:Tri-Sync 外汇交易系统 核心理念 「基本面定方…...
Linux之守护进程
在Linux系统中,进程一般分为前台进程、后台进程和守护进程3类。 一 守护进程 定义: 1.守护进程是在操作系统后台运行的一种特殊类型的进程,它独立于前台用户界面,不与任何终端设备直接关联。这些进程通常在系统启动时启动,并持…...

LiquiGen流体导入UE
导出ABC 导出贴图 ABC导入Houdini UE安装SideFX_Labs插件 C:\Users\Star\Documents\houdini20.5\SideFXLabs\unreal\5.5 参考: LiquiGenHoudiniUE血液流程_哔哩哔哩_bilibili...
使用react进行用户管理系统
今天通了一遍使用react进行用户管理系统的文档,以及跟随步骤实现了一遍,我大概梳理一下实现思路。 首先我们构建基本用户管理应用,需要数据库存储个人资料,我们先去supabase注册然后创建自己的数据库然后设置密码,然后…...
SpringBoot的java应用中,慢sql会导致CPU暴增吗
是的,在 Spring Boot 的 Java 应用中,慢 SQL 同样可能导致 CPU 暴增。虽然数据库服务器的 CPU 通常是主要压力点,但应用服务器(Java 进程)的 CPU 也可能间接受到影响,具体原因和机制如下: 1. 数…...

Ubuntu下编译mininim游戏全攻略
目录 一、安装mininim 软件所依赖的库(重点是allegro游戏引擎库)二、编译mininim 软件三、将mininim打包给另一个Ubuntu系统使用四、安卓手机运行mininim 一、安装mininim 软件所依赖的库(重点是allegro游戏引擎库) 1. 用apt-get…...

uniapp uni-id Error: Invalid password secret
common文件夹下uni-config-center文件夹下新建uni-id,新建config.json文件 复制粘贴以下代码,不要自己改,格式容易错 {"passwordSecret": [{"type": "hmac-sha256","version": 1}], "passwordStrength&qu…...
用 Appuploader,让 iOS 上架流程真正“可交接、可记录、可复用”:我们是这样实现的
你可能听说过这样一类人:上线必找他,证书只有他有,Transporter 密码在他电脑上,描述文件什么时候过期,只有他知道。 如果你团队里有这样一位“发布大师”,他可能是个英雄——但也是个单点风险源。 我们团…...

第十二节:第三部分:集合框架:List系列集合:特点、方法、遍历方式、ArrayList集合的底层原理
List系列集合特点 List集合的特有方法 List集合支持的遍历方式 ArrayList集合的底层原理 ArrayList集合适合的应用场景 代码:List系列集合遍历方式 package com.itheima.day19_Collection_List;import java.util.ArrayList; import java.util.Iterator; import jav…...

【办公类-18-07】20250527屈光检查PDF文件拆分成多个pdf(两页一份,用幼儿班级姓名命名文件)
背景需求: 今天春游,上海海昌公园。路上保健老师收到前几天幼儿的屈光视力检查单PDF。 她说:所有孩子的通知都做在一个PDF里,我没法单独发给班主任。你有什么办法拆开来? 我说:“没问题,问deep…...

AI Agent的“搜索大脑“进化史:从Google API到智能搜索生态的技术变革
AI Agent搜索革命的时代背景 2025年agent速度发展之快似乎正在验证"2025年是agent元年"的说法,而作为agent最主要的应用工具之一(另外一个是coding),搜索工具也正在呈现快速的发展趋势。Google在2024年12月推出Gemini Deep Research࿰…...

Arduino学习-跑马灯
1、效果 2、代码 /**** 2025-5-30 跑马灯的小程序 */ //时间间隔 int intervaltime200; //初始化函数 void setup() {// put your setup code here, to run once://设置第3-第7个引脚为输出模式for(int i3;i<8;i){pinMode(i,OUTPUT);} }//循环执行 void loop() {// put you…...
python创建args命令行分析
这段代码是一个使用 Python 的 argparse 模块创建命令行界面的示例。它定义了一系列的命令行参数和子命令,通常用于构建和管理软件项目或版本控制系统中的操作。以下是对代码的逐行分析: 1初始化 ArgumentParser parser argparse.ArgumentParser(forma…...

2. 手写数字预测 gui版
2. 手写数字预测 gui版 背景1.界面绘制2.处理图片3. 加载模型4. 预测5.结果6.一点小问题 背景 做了手写数字预测的模型,但是老是跑模型太无聊了,就配合pyqt做了一个可视化界面出来玩一下 源代码可以去这里https://github.com/Leezed525/pytorch_toy拿 …...
js数据类型有哪些?它们有什么区别?
js数据类型共有8种,分别是undefined,null,boolean,number,string,Object,symbol,bigint symbol和bigint是es6中提出来的数据类型 symbol创建后独一无二不可变的数据类型,它主要是为了解决出现全局变量冲突的问题 bigint 是一种数字类型的数据,它可以表示任意精度格式的整数,…...
大模型应用开发第五讲:成熟度模型:从ChatGPT(L2)到未来自主Agent(L4)
大模型应用开发第五讲:成熟度模型:从ChatGPT(L2)到未来自主Agent(L4) 资料取自《大模型应用开发:动手做AI Agent 》。 查看总目录:学习大纲 关于DeepSeek本地部署指南可以看下我之…...

特别篇-产品经理(三)
一、市场与竞品分析—竞品分析 1. 课后总结 案例框架:通过"小新吃蛋糕"案例展示行业分析方法,包含四个关键步骤: 明确目标行业调研确定竞品分析竞争策略输出结论 1)行业背景分析方法 PEST分析法:从四个…...
IP地址扫描 网络状态监测 企业网络管理 免安装,企业级 IP 监控防未授权接入
各位网络小卫士们!今天咱来聊聊一款超厉害的局域网IP地址扫描工具——IPScaner V1.22。这玩意儿就像网络世界的大侦探,能快速识别网络里设备的状态和资源分布。下面咱就好好唠唠它的那些事儿。 软件获取夸克网盘下载 先说说它的核心功能。第一个是IP…...

【unity游戏开发——编辑器扩展】AssetDatabase公共类在编辑器环境中管理和操作项目中的资源
注意:考虑到编辑器扩展的内容比较多,我将编辑器扩展的内容分开,并全部整合放在【unity游戏开发——编辑器扩展】专栏里,感兴趣的小伙伴可以前往逐一查看学习。 文章目录 前言一、AssetDatabase常用API1、创建资源1.1 API1.2 示例 …...

BLE协议全景图:从0开始理解低功耗蓝牙
BLE(Bluetooth Low Energy)作为一种针对低功耗场景优化的通信协议,已经广泛应用于智能穿戴、工业追踪、智能家居、医疗设备等领域。 本文是《BLE 协议实战详解》系列的第一篇,将从 BLE 的发展历史、协议栈结构、核心机制和应用领域出发,为后续工程实战打下全面认知基础。 …...

【机器学习基础】机器学习入门核心算法:GBDT(Gradient Boosting Decision Tree)
机器学习入门核心算法:GBDT(Gradient Boosting Decision Tree) 1. 算法逻辑2. 算法原理与数学推导2.1 目标函数2.2 负梯度计算2.3 决策树拟合2.4 叶子权重计算2.5 模型更新 3. 模型评估评估指标防止过拟合 4. 应用案例4.1 金融风控4.2 推荐系…...

基于开源AI大模型AI智能名片S2B2C商城小程序源码的销售环节数字化实现路径研究
摘要:在数字化浪潮下,企业销售环节的转型升级已成为提升竞争力的核心命题。本文基于清华大学全球产业研究院《中国企业数字化转型研究报告(2020)》提出的“提升销售率与利润率、打通客户数据、强化营销协同、构建全景用户画像、助…...

Spring Cache核心原理与快速入门指南
文章目录 前言一、Spring Cache核心原理1.1 架构设计思想1.2 运行时执行流程1.3 核心组件协作1.4 关键机制详解1.5 扩展点设计1.6 与Spring事务的协同 二、快速入门实战三、局限性3.1 多级缓存一致性缺陷3.2 分布式锁能力缺失3.3 事务集成陷阱 总结 前言 在当今高并发、低延迟…...

Redisson学习专栏(四):实战应用(分布式会话管理,延迟队列)
文章目录 前言一、为什么需要分布式会话管理?1.1 使用 Redisson 实现 Session 共享 二、订单超时未支付?用延迟队列精准处理2.1 RDelayedQueue 核心机制2.2 订单超时处理实战 总结 前言 在现代分布式系统中,会话管理和延迟任务处理是两个核心…...

java程序从服务器端到Lambda函数的迁移与优化
source:https://www.jfokus.se/jfokus24-preso/From-Serverful-to-Serverless-Java.pdf 从传统的服务器端Java应用,到如今的无服务器架构。这不仅仅是技术名词的改变,更是开发模式和运维理念的一次深刻变革。先快速回顾一下我们熟悉的“服务…...

使用yocto搭建qemuarm64环境
环境 yocto下载 # 源码下载 git clone git://git.yoctoproject.org/poky git reset --hard b223b6d533a6d617134c1c5bec8ed31657dd1268 构建 # 编译镜像 export MACHINE"qemuarm64" . oe-init-build-env bitbake core-image-full-cmdline 运行 # 跑虚拟机 export …...
Vue 3前沿生态整合:WebAssembly与TypeScript深度实践
一、Vue 3 WebAssembly:突破性能天花板 01、WebAssembly:浏览器中的原生性能 WebAssembly(Wasm)是一种可在现代浏览器中运行的二进制指令格式,其性能接近原生代码。结合Vue 3的响应式架构,我们可以在前端…...

Linux系统下安装配置 Nginx
Windows Nginx https://nginx.org/en/download.htmlLinux Nginx https://nginx.org/download/nginx-1.24.0.tar.gz解压 tar -zxvf tar -zxvf nginx-1.18.0.tar.gz #解压安装依赖(如未安装) yum groupinstall "Development Tools" -y yum…...