当前位置: 首页 > article >正文

Android第十一次面试多线程篇

面试官​:
“你在项目里用过Handler吗?能说说它是怎么工作的吗?”

候选人​:
“当然用过!比如之前做下载功能时,需要在后台线程下载文件,然后在主线程更新进度条。这时候就得用Handler来切回主线程。简单来说,Handler就像个快递员,负责把子线程的任务包裹(比如更新UI的指令)送到主线程去执行。”

面试官追问​:
“比喻挺有意思。那这个‘快递员’是怎么把消息从子线程送到主线程的?”

候选人​:
“其实核心是靠三个东西:HandlerLooperMessageQueue。比如主线程启动时,系统会默认创建一个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是在ActivityThreadmain()方法里初始化的。大概流程是:”

  1. 启动App​:系统调用ActivityThread.main()
  2. 准备Looper​:调用Looper.prepareMainLooper(),创建主线程的Looper和消息队列。
  3. 开启循环​:调用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()
  • 阻塞唤醒机制​:
    • 队列为空时,通过 epollpipe 进入休眠。
    • 新消息入队时唤醒线程(通过 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)​​:
    管理协程生命周期(如viewModelScopelifecycleScope),确保任务不会泄漏。
    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捕获所有异步错误

五、协程的典型应用场景
  1. 网络请求​:
    suspend fun loadData() = withContext(Dispatchers.IO) {retrofitService.fetchData()
    }
  2. 数据库操作​:
    fun insertUser(user: User) = viewModelScope.launch(Dispatchers.IO) {database.userDao().insert(user)
    }
  3. 并发任务组合​:
    val result1 = async { task1() }
    val result2 = async { task2() }
    val combined = result1.await() + result2.await() // 并行执行
  4. 超时与重试​:
    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开发?”

候选人​:
“三个字——快、稳、省!

  1. ​:代码写起来像同步,维护效率高,再也不用和回调地狱搏斗。
  2. ​:结构化并发自动管理生命周期,结合viewModelScope/lifecycleScope,内存泄漏少一半。
  3. ​:一个线程跑几百个协程,用过的都说像‘开挂’,尤其低端机上效果明显。”

面试官​:
“回答得很到位。你还有什么问题吗?”

候选人​:
“咱们团队用协程遇到过印象深刻的坑吗?比如和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第十一次面试多线程篇

​面试官​&#xff1a; “你在项目里用过Handler吗&#xff1f;能说说它是怎么工作的吗&#xff1f;” ​候选人​&#xff1a; “当然用过&#xff01;比如之前做下载功能时&#xff0c;需要在后台线程下载文件&#xff0c;然后在主线程更新进度条。这时候就得用Handler来切…...

安全,稳定可靠的政企即时通讯数字化平台

在当今数字化时代&#xff0c;政企机构面临着复杂多变的业务需求和日益增长的沟通协作挑战。BeeWorks作为一款安全&#xff0c;稳定可靠的政企即时通讯数字化平台&#xff0c;凭借其安全可靠、功能强大的特性&#xff0c;为政企提供了高效、便捷的沟通协作解决方案&#xff0c;…...

craw4ai 抓取实时信息,与 mt4外行行情结合实时交易,基本面来觉得趋势方向,搞一个外汇交易策略

结合实时信息抓取、MT4行情数据、基本面分析的外汇交易策略框架&#xff0c;旨在通过多维度数据融合提升交易决策质量&#xff1a;行不行不知道先试试&#xff0c;理论是对的&#xff0c;只要基本面方向没错 策略名称&#xff1a;Tri-Sync 外汇交易系统 核心理念 「基本面定方…...

Linux之守护进程

在Linux系统中&#xff0c;进程一般分为前台进程、后台进程和守护进程3类。 一 守护进程 定义: 1.守护进程是在操作系统后台运行的一种特殊类型的进程&#xff0c;它独立于前台用户界面&#xff0c;不与任何终端设备直接关联。这些进程通常在系统启动时启动&#xff0c;并持…...

LiquiGen流体导入UE

导出ABC 导出贴图 ABC导入Houdini UE安装SideFX_Labs插件 C:\Users\Star\Documents\houdini20.5\SideFXLabs\unreal\5.5 参考: LiquiGenHoudiniUE血液流程_哔哩哔哩_bilibili...

使用react进行用户管理系统

今天通了一遍使用react进行用户管理系统的文档&#xff0c;以及跟随步骤实现了一遍&#xff0c;我大概梳理一下实现思路。 首先我们构建基本用户管理应用&#xff0c;需要数据库存储个人资料&#xff0c;我们先去supabase注册然后创建自己的数据库然后设置密码&#xff0c;然后…...

SpringBoot的java应用中,慢sql会导致CPU暴增吗

是的&#xff0c;在 Spring Boot 的 Java 应用中&#xff0c;慢 SQL 同样可能导致 CPU 暴增。虽然数据库服务器的 CPU 通常是主要压力点&#xff0c;但应用服务器&#xff08;Java 进程&#xff09;的 CPU 也可能间接受到影响&#xff0c;具体原因和机制如下&#xff1a; 1. 数…...

Ubuntu下编译mininim游戏全攻略

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

uniapp uni-id Error: Invalid password secret

common文件夹下uni-config-center文件夹下新建uni-id,新建config.json文件 复制粘贴以下代码&#xff0c;不要自己改&#xff0c;格式容易错 {"passwordSecret": [{"type": "hmac-sha256","version": 1}], "passwordStrength&qu…...

用 Appuploader,让 iOS 上架流程真正“可交接、可记录、可复用”:我们是这样实现的

你可能听说过这样一类人&#xff1a;上线必找他&#xff0c;证书只有他有&#xff0c;Transporter 密码在他电脑上&#xff0c;描述文件什么时候过期&#xff0c;只有他知道。 如果你团队里有这样一位“发布大师”&#xff0c;他可能是个英雄——但也是个单点风险源。 我们团…...

第十二节:第三部分:集合框架:List系列集合:特点、方法、遍历方式、ArrayList集合的底层原理

List系列集合特点 List集合的特有方法 List集合支持的遍历方式 ArrayList集合的底层原理 ArrayList集合适合的应用场景 代码&#xff1a;List系列集合遍历方式 package com.itheima.day19_Collection_List;import java.util.ArrayList; import java.util.Iterator; import jav…...

【办公类-18-07】20250527屈光检查PDF文件拆分成多个pdf(两页一份,用幼儿班级姓名命名文件)

背景需求&#xff1a; 今天春游&#xff0c;上海海昌公园。路上保健老师收到前几天幼儿的屈光视力检查单PDF。 她说&#xff1a;所有孩子的通知都做在一个PDF里&#xff0c;我没法单独发给班主任。你有什么办法拆开来&#xff1f; 我说&#xff1a;“没问题&#xff0c;问deep…...

AI Agent的“搜索大脑“进化史:从Google API到智能搜索生态的技术变革

AI Agent搜索革命的时代背景 2025年agent速度发展之快似乎正在验证"2025年是agent元年"的说法&#xff0c;而作为agent最主要的应用工具之一(另外一个是coding)&#xff0c;搜索工具也正在呈现快速的发展趋势。Google在2024年12月推出Gemini Deep Research&#xff0…...

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 模块创建命令行界面的示例。它定义了一系列的命令行参数和子命令&#xff0c;通常用于构建和管理软件项目或版本控制系统中的操作。以下是对代码的逐行分析&#xff1a; 1初始化 ArgumentParser parser argparse.ArgumentParser(forma…...

2. 手写数字预测 gui版

2. 手写数字预测 gui版 背景1.界面绘制2.处理图片3. 加载模型4. 预测5.结果6.一点小问题 背景 做了手写数字预测的模型&#xff0c;但是老是跑模型太无聊了&#xff0c;就配合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)

大模型应用开发第五讲&#xff1a;成熟度模型&#xff1a;从ChatGPT&#xff08;L2&#xff09;到未来自主Agent&#xff08;L4&#xff09; 资料取自《大模型应用开发&#xff1a;动手做AI Agent 》。 查看总目录&#xff1a;学习大纲 关于DeepSeek本地部署指南可以看下我之…...

特别篇-产品经理(三)

一、市场与竞品分析—竞品分析 1. 课后总结 案例框架&#xff1a;通过"小新吃蛋糕"案例展示行业分析方法&#xff0c;包含四个关键步骤&#xff1a; 明确目标行业调研确定竞品分析竞争策略输出结论 1&#xff09;行业背景分析方法 PEST分析法&#xff1a;从四个…...

IP地址扫描 网络状态监测 企业网络管理 免安装,企业级 IP 监控防未授权接入

各位网络小卫士们&#xff01;今天咱来聊聊一款超厉害的局域网IP地址扫描工具——IPScaner V1.22。这玩意儿就像网络世界的大侦探&#xff0c;能快速识别网络里设备的状态和资源分布。下面咱就好好唠唠它的那些事儿。 软件获取夸克网盘下载 先说说它的核心功能。第一个是IP…...

【unity游戏开发——编辑器扩展】AssetDatabase公共类在编辑器环境中管理和操作项目中的资源

注意&#xff1a;考虑到编辑器扩展的内容比较多&#xff0c;我将编辑器扩展的内容分开&#xff0c;并全部整合放在【unity游戏开发——编辑器扩展】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 前言一、AssetDatabase常用API1、创建资源1.1 API1.2 示例 …...

BLE协议全景图:从0开始理解低功耗蓝牙

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

【机器学习基础】机器学习入门核心算法:GBDT(Gradient Boosting Decision Tree)

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

基于开源AI大模型AI智能名片S2B2C商城小程序源码的销售环节数字化实现路径研究

摘要&#xff1a;在数字化浪潮下&#xff0c;企业销售环节的转型升级已成为提升竞争力的核心命题。本文基于清华大学全球产业研究院《中国企业数字化转型研究报告&#xff08;2020&#xff09;》提出的“提升销售率与利润率、打通客户数据、强化营销协同、构建全景用户画像、助…...

Spring Cache核心原理与快速入门指南

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

Redisson学习专栏(四):实战应用(分布式会话管理,延迟队列)

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

java程序从服务器端到Lambda函数的迁移与优化

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

使用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&#xff1a;突破性能天花板 01、WebAssembly&#xff1a;浏览器中的原生性能 WebAssembly&#xff08;Wasm&#xff09;是一种可在现代浏览器中运行的二进制指令格式&#xff0c;其性能接近原生代码。结合Vue 3的响应式架构&#xff0c;我们可以在前端…...

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 #解压安装依赖&#xff08;如未安装&#xff09; yum groupinstall "Development Tools" -y yum…...