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

【黑马点评日记02】:Session+ThreadLocal实现短信登录

个人主页北极的代码欢迎来访作者简介java后端学习者❄️个人专栏苍穹外卖日记SSM框架深入JavaWeb✨命运的结局尽可永在不屈的挑战却不可须臾或缺前言前一天我们导入了黑马点评项目并配置好了环境那么从今天开始我们就要开始完成项目的相关业务操作了今天实现的是短信登录功能。摘要本文介绍了基于Session和ThreadLocal的短信验证码登录实现方案。主要内容包括1. 验证码登录流程分为发送验证码和登录注册两个阶段验证码和用户信息存储在Session中2. 使用ThreadLocal作为临时用户信息传递工具避免代码臃肿3. 详细阐述了Session存储验证码和用户信息的必要性及优势4. 提供了完整的代码实现示例包括发送验证码、登录注册接口及拦截器实现5. 强调了使用UserDTO而非User实体类来保护敏感信息。该方案利用Session维持登录状态通过ThreadLocal实现用户信息在请求各层间的优雅传递关于Session和ThreadLocal基于Session的短信验证码登录流程主要分为两个阶段发送验证码和登录注册。1. 发送验证码流程前端用户输入手机号点击“获取验证码”。后端接收手机号进行基本校验非空、格式。生成一个随机6位数字验证码如123456。关键步骤将验证码存入HttpSession。Key一个固定字符串或与手机号关联的Key例如“code”或“logincode”phone。Value验证码本身。调用短信服务商API发送验证码生产环境。在测试/教学阶段通常直接打印到控制台或模拟发送。返回成功提示。2. 登录注册流程前端用户输入手机号 收到的验证码点击登录。后端接收手机号和验证码。从Session中获取之前存储的验证码根据相同的Key。校验判断用户提交的验证码 是否等于 Session中存储的验证码并且验证码是否过期Session有超时时间。不一致或不存在返回“验证码错误”。一致继续。查询用户根据手机号查询数据库。分支处理已存在用户直接获取用户信息。不存在用户注册创建新用户填充手机号、默认昵称如user_随机数、默认头像等保存到数据库然后获取这个新用户。登录态保持将查询到的用户对象或用户信息如ID、昵称、头像存入Session。Key例如“user”。Value用户对象。返回成功通常前端会跳转到首页。3.校验登陆状态【用户请求】GET /user/order ↓ ① 请求进入 Tomcat携带 Cookie: JSESSIONIDABC123 ↓ ② Spring MVC 查找对应的拦截器 ↓ ③ 拦截器 preHandle 方法执行 ↓ ④ 从 Session 获取用户信息 ↓ ⑤ 判断是否已登录 ↓ ┌─────────────────┴─────────────────┐ ↓ ↓ 【已登录】 【未登录】 ↓ ↓ 存入 ThreadLocal 返回 401 状态码 ↓ ↓ 放行到 Controller 拦截不进入 Controller ↓ ↓ 执行业务逻辑 响应返回前端 ↓ 返回数据给前端关键区分存储位置 vs. 传递方式概念存储位置生命周期作用Session 存储用户服务器内存Tomcat会话级别30分钟跨请求共享用户信息ThreadLocal 存储用户当前线程的内存单次请求级别请求结束即清理在当前请求的各个方法间传递用户信息ThreadLocal 不是替代 Session 的存储方案而是 Session 的临时搬运工。完整流程解析【请求到达】携带 Cookie: JSESSIONIDABC123 ↓ 【Tomcat 根据 JSESSIONID 找到 Session】 Session 内容: {user: User对象(张三)} ↓ 【拦截器 preHandle】 session.getAttribute(user) → 拿到 User(张三) UserHolder.setUser(张三) ← 存入 ThreadLocal ↓ 【Controller 方法】 UserHolder.getUser() → 拿到 User(张三) ← 从 ThreadLocal 取 处理业务... ↓ 【Service 层】 UserHolder.getUser() → 仍然拿到 User(张三) ← 同一个线程 处理业务... ↓ 【拦截器 afterCompletion】 UserHolder.remove() ← 清理 ThreadLocal防止内存泄漏 ↓ 【响应返回给客户端】关键点用户信息的主存储位置是 Session服务器内存ThreadLocal 只是当前请求的临时缓存方便各层代码获取用户请求结束后ThreadLocal 会被清理但 Session 中的用户还在方案代码示例问题不用 ThreadLocalController:session.getAttribute(user)Service: 需要传入 user 参数Mapper: 需要继续传参数到处传递代码臃肿用 ThreadLocal任何地方直接UserHolder.getUser()优雅、解耦核心问题为什么要把验证码和用户放Session里这是基于HTTP协议的无状态性和Session的服务端存储机制决定的。1. 为什么验证码放Session里核心目的将“服务端发出的验证码”与“当前用户的浏览器会话”绑定防止跨请求伪造和盗用。身份绑定你给手机号138****0000发了验证码123456。这个验证码只应该属于这个用户的这次请求。Session天然与一个特定用户通过Cookie中的JSESSIONID标识绑定。当用户提交验证码时服务端从该用户的Session中取出验证码进行比对确保了“这个验证码确实是发给这个浏览器的”。避免全局冲突如果把验证码放Redis内存数据库或内存Map中并且只用手机号做Key会出现问题用户A的手机App和电脑网页同时登录后一次请求会覆盖前一次的验证码。而Session隔离了不同会话即使是同一手机号、不同浏览器也是不同Session。自动过期Session本身有超时时间如30分钟验证码存在Session里会随着Session过期而自动失效无需额外编写过期删除逻辑。验证码的安全生命周期自然得到保证。安全性验证码是敏感信息绝不应该放在前端Cookie、LocalStorage或URL参数中让客户端保存。放在服务端Session是最安全的方式之一客户端根本无法获取到验证码明文。2. 为什么用户信息放Session里核心目的实现登录态跟踪避免每次请求都查询数据库。证明“已登录”用户登录后后续请求如查询订单、点赞如何知道是谁发起的服务端如何知道这个请求是“已登录”用户——通过Session中是否存在用户对象来判断。存在且有效即视为已登录。减少DB查询用户登录后每个请求可能都需要获取用户信息如展示昵称、头像、权限校验。如果每次都从数据库查在高并发下压力很大。把用户信息放入Session即服务端内存后续请求直接从Session中快速取出性能极高。状态维持HTTP是无状态的Session机制在服务端维持了状态。用户信息放入Session等同于在服务端标记了“这个会话对应的用户是张三”。后续请求带着JSESSIONID来服务端就知道是张三。安全控制可以方便地实现踢人下线、强制过期等管理功能。例如管理员注销某个用户时可以找到并销毁其Session。代码实现短信验证码的发送接口设计项目内容接口名称发送短信验证码请求方式POST请求路径/user/code请求参数phone手机号响应格式JSON核心逻辑校验手机号 → 生成验证码 → 存入Session → 发送短信模拟Controller/** * 发送手机验证码 */ PostMapping(code) public Result sendCode(RequestParam(phone) String phone, HttpSession session) { // 发送短信验证码并保存验证码 return userService.sendCode(phone,session); }Service实现Controller没有的方法然后在这里实现具体的业务操作/** * 发送验证码 * param phone * param session * return */ public Result sendCode(String phone, HttpSession session) { //校验手机号 if(RegexUtils.isPhoneInvalid(phone)){ return Result.fail(手机号格式错误); } //生成验证码 String code RandomUtil.randomNumbers(6); //保存验证码 session.setAttribute(code,code); //发送验证码模拟 System.out.println(发送验证码成功code); return Result.ok(); }代码实现登录和注册接口设计项目内容接口名称短信验证码登录/注册请求方式POST请求路径/user/login请求参数phone手机号、code验证码响应格式JSON核心逻辑校验验证码 → 查询用户 → 不存在则注册 → 存入SessionController/** * 登录功能 * param loginForm 登录参数包含手机号、验证码或者手机号、密码 */ PostMapping(/login) public Result login(RequestBody LoginFormDTO loginForm, HttpSession session){ // 实现登录功能 return userService.login(loginForm,session); }Service/** * 登录和注册功能 * param loginForm * param session * return */ public Result login(LoginFormDTO loginForm, HttpSession session) { //校验手机号 if(RegexUtils.isPhoneInvalid(loginForm.getPhone())){ return Result.fail(手机号格式错误); } //校验验证码 String cacheCode (String)session.getAttribute(code); String code loginForm.getCode(); if(cacheCodenull||!cacheCode.equals(code)){ return Result.fail(验证码错误); } //查询用户 User user query().eq(phone, loginForm.getPhone()).one(); //判断用户是否存在 if(usernull){ //不存在创建新用户并保存 user createUserWithPhone(loginForm.getPhone()); } //保存用户 session.setAttribute(user,user); return null; } private User createUserWithPhone(String phone) { //创建用户 User usernew User(); user.setPhone(phone); user.setNickName(RandomUtil.randomString(10)); //保存用户 save(user); return user; }Mybatis-Plus关于这里保存新用户到数据库中的方法我们用的是Mybatis-Plusquery().eq(phone, loginForm.getPhone()).one();save(user);Mybatis-Plus对比传统 MyBatisMyBatis-Plus插入数据写 XML 或注解 SQL直接调用save(user)查询数据写SELECT * FROM user WHERE id ?调用selectById(id)更新数据写UPDATE user SET ...调用updateById(user)删除数据写DELETE FROM user WHERE ...调用deleteById(id)2.save(user)是什么意思java save(user); // 把 user 对象插入到数据库等价于这条 SQLsql INSERT INTO user (phone, nick_name, create_time, update_time) VALUES (13800138000, user_abc123, 2024-01-15 10:30:00, 2024-01-15 10:30:00)3. 完整代码示例java // 你的代码 private User createUserWithPhone(String phone) { User user new User(); user.setPhone(phone); user.setNickName(user_ RandomUtil.randomString(8)); user.setCreateTime(LocalDateTime.now()); user.setUpdateTime(LocalDateTime.now()); save(user); // ← 这行代码把 user 存进数据库 return user; }执行过程text user 对象内存中 ↓ save(user) ↓ MyBatis-Plus 自动生成 SQL ↓ INSERT INTO user (phone, nick_name, ...) VALUES (?, ?, ...) ↓ 数据库执行插入 ↓ 数据库里多了一条用户记录4.save(user)的返回值java boolean success save(user); // true插入成功false失败5. 为什么不用写 SQLMyBatis-Plus 通过实体类映射自动生成 SQLjava // User 实体类 Data TableName(user) // 指定表名 public class User { TableId(type IdType.AUTO) // 主键自增 private Long id; private String phone; // 对应数据库 phone 字段 private String nickName; // 对应数据库 nick_name 字段 private LocalDateTime createTime; private LocalDateTime updateTime; }MyBatis-Plus 看到save(user)会读取TableName(user)→ 知道要插入user表读取对象的字段值 → 提取phone、nickName等值自动生成 INSERT SQL 并执行MyBatis-Plus 常用方法速查继承ServiceImpl后可以直接用的方法java Service public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService { public void demo() { // 插入 save(user); // 插入一条 saveBatch(userList); // 批量插入 // 删除 removeById(1L); // 根据 ID 删除 removeByIds(Arrays.asList(1,2,3)); // 批量删除 // 更新 updateById(user); // 根据 ID 更新 update(user, updateWrapper); // 条件更新 // 查询 getById(1L); // 根据 ID 查询 list(); // 查询所有 listByIds(Arrays.asList(1,2)); // 批量查询 count(); // 查询总数 // 条件查询Lambda 方式 query().eq(phone, 13800138000).one(); // 等值查询单条 query().eq(phone, phone).list(); // 等值查询多条 lambdaQuery().eq(User::getPhone, phone).one(); // Lambda 方式推荐 } }代码实现登陆校验拦截器拦截器在请求到达 Controller 之前先判断用户是否已登录未登录则直接拦截返回不让访问需要登录的接口。用户请求 ↓ 拦截器preHandle ↓ 判断 Session 中是否有 user ↓ ┌─────────────┴─────────────┐ ↓ ↓ 有已登录 无未登录 ↓ ↓ 放行进入 Controller 拦截返回 401自定义类LoginInterceptor实现接口的前置拦截和后置拦截Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //获取session HttpSession session request.getSession(); //获取session的用户 Object user session.getAttribute(user); //判断用户是否存在 //如果不存在拦截 if (usernull){ response.setStatus(401); return false; } //若存在则保存到ThreadLocal User loginUser (User) user; UserDTO userDTO new UserDTO(); userDTO.setId(loginUser.getId()); userDTO.setNickName(loginUser.getNickName()); userDTO.setIcon(loginUser.getIcon()); UserHolder.saveUser(userDTO); return true; }配置拦截器Configuration public class MvcConfig implements WebMvcConfigurer { /** * 添加拦截器 * param registry */ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .excludePathPatterns( /shop/**, /voucher/**, /shop-type/**, /upload/**, /blog/hot, /user/code, /user/login ); WebMvcConfigurer.super.addInterceptors(registry); } }User对象可能包含password、phone等敏感字段直接返回给前端会泄露隐私因此我们返回给前端的不能是User对象而是UserDTO因此我们把在登录用户时存入session的用户类型变成DTO通过对象属性拷贝来给DTO里面的属性赋值session.setAttribute(user, BeanUtil.copyProperties(user, UserDTO.class));之后我们在拦截器和Controller接口返回的都是UserDTO不包含敏感信息【登录时】 数据库 User 实体包含 password、phone 等所有字段 ↓ 转换为 UserDTO只包含 id、nickName、icon 等公开字段 ↓ session.setAttribute(user, userDTO) ← Session 中存的是 UserDTO ↓ 【后续请求】 拦截器从 Session 取出 ↓ UserDTO userDTO (UserDTO) session.getAttribute(user) ← 取出的是 UserDTO ↓ UserHolder.setUser(userDTO) ← ThreadLocal 存的是 UserDTO ↓ Controller/Service 中使用 ↓ UserDTO currentUser UserHolder.getUser() ← 拿到的也是 UserDTO ↓ 返回给前端只包含公开字段安全结语如果对你有帮助请点赞关注收藏你的支持就是我最大的鼓励

相关文章:

【黑马点评日记02】:Session+ThreadLocal实现短信登录

🔥个人主页:北极的代码(欢迎来访) 🎬作者简介:java后端学习者 ❄️个人专栏:苍穹外卖日记,SSM框架深入,JavaWeb ✨命运的结局尽可永在,不屈的挑战却不可须臾或…...

nli-distilroberta-base多场景:学术论文摘要与引言部分逻辑支撑关系分析

nli-distilroberta-base多场景:学术论文摘要与引言部分逻辑支撑关系分析 1. 项目概述 nli-distilroberta-base是一个基于DistilRoBERTa模型的自然语言推理(NLI)Web服务,专门用于分析两个句子之间的逻辑关系。这个轻量级但功能强大的工具可以帮助研究人…...

ClearerVoice-Studio企业级方案:基于SpringBoot的智能客服语音优化系统

ClearerVoice-Studio企业级方案:基于SpringBoot的智能客服语音优化系统 1. 引言 想象一下这样的场景:客服中心每天处理成千上万的客户来电,但通话质量却参差不齐。有的客户在嘈杂的街头打电话,背景是车水马龙的噪音;…...

5分钟掌握百度网盘提取码智能获取:告别繁琐搜索的高效解决方案

5分钟掌握百度网盘提取码智能获取:告别繁琐搜索的高效解决方案 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘资源提取码而烦恼吗?baidupankey作为一款专业的提取码智能获取工具&#x…...

SGLang-v0.5.6环境配置全解析:从Python版本到模型路径设置

SGLang-v0.5.6环境配置全解析:从Python版本到模型路径设置 1. 环境准备:Python与系统配置 1.1 Python版本要求与验证 SGLang-v0.5.6需要Python 3.10或更高版本才能正常运行。这是因为它使用了Python 3.10引入的新语法特性,如结构化模式匹配等…...

Qwen3.5-35B-A3B-AWQ-4bit镜像免配置教程:内置模型目录+服务自动恢复

Qwen3.5-35B-A3B-AWQ-4bit镜像免配置教程:内置模型目录服务自动恢复 1. 模型介绍 Qwen3.5-35B-A3B-AWQ-4bit是一个专为视觉多模态理解设计的量化模型,特别适合需要图片分析和图文对话的应用场景。这个镜像已经内置了完整的模型目录,部署后即…...

基于Git版本管理的CasRel模型迭代实验记录规范

基于Git版本管理的CasRel模型迭代实验记录规范 做机器学习项目,尤其是像CasRel这样的关系抽取模型,最头疼的往往不是调参本身,而是实验管理。今天调了个学习率,明天改了下网络结构,后天又换了预处理方式。过了一周&am…...

Phi-3-mini-4k-instruct-gguf免配置环境:支持HTTPS反向代理与Basic Auth安全加固

Phi-3-mini-4k-instruct-gguf免配置环境:支持HTTPS反向代理与Basic Auth安全加固 1. 平台介绍 Phi-3-mini-4k-instruct-gguf是微软Phi-3系列中的轻量级文本生成模型GGUF版本,特别适合问答、文本改写、摘要整理和简短创作等场景。这个预配置的镜像已经完…...

终极指南:如何免费使用CefFlashBrowser让经典Flash游戏重获新生

终极指南:如何免费使用CefFlashBrowser让经典Flash游戏重获新生 【免费下载链接】CefFlashBrowser Flash浏览器 / Flash Browser 项目地址: https://gitcode.com/gh_mirrors/ce/CefFlashBrowser 还在为无法重温童年Flash游戏而烦恼吗?当主流浏览器…...

5秒破解百度网盘提取码:智能获取工具的终极指南

5秒破解百度网盘提取码:智能获取工具的终极指南 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘加密资源而烦恼吗?当你在网上找到心仪的学习资料或软件资源,却被"请输入…...

分散加载详解与应用

分散加载(Scatter Loading)是一种由链接器(Linker)提供的、用于精确控制程序各个段(如代码、数据)在目标存储器(如 Flash、RAM)中加载地址和执行地址的机制。其核心在于将单一的、线…...

Flutter 三方库 get\_it + injectable 的鸿蒙化适配指南:实现优雅的依赖注入

Flutter 三方库 get_it injectable 的鸿蒙化适配指南:实现优雅的依赖注入 欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net 大家好呀!🌸 今天要和大家分享一个超级实用的Flutter开发技巧——如何将 get_i…...

亚马逊品牌推广:破局只曝光不转化误区,解锁拿单新思路

亚马逊品牌推广:破局只曝光不转化误区,解锁拿单新思路 正文: 新品上线 6 个月仅投商品推广,核心词 CPC 一路走高,ACOS居高不下,冷启动慢、迟迟起不了量?不少亚马逊卖家都面临这样的困境&#xf…...

别再让RAG乱给答案了!手把手教你用Cohere Rerank给LangChain检索结果‘排座次’

用Cohere Rerank重构LangChain检索逻辑:从混沌到精准的实战指南 当你发现自己的RAG系统开始像醉酒的水手一样胡言乱语时,是时候给那些混乱的检索结果"排座次"了。作为一名长期与LangChain打交道的开发者,我经历过无数次检索结果相关…...

3分钟掌握电脑性能优化:开源工具UXTU终极指南

3分钟掌握电脑性能优化:开源工具UXTU终极指南 【免费下载链接】Universal-x86-Tuning-Utility Unlock the full potential of your Intel/AMD based device. 项目地址: https://gitcode.com/gh_mirrors/un/Universal-x86-Tuning-Utility 你是否曾经感觉自己的…...

UNIT-00模型在ComfyUI工作流中的插件开发与应用

UNIT-00模型在ComfyUI工作流中的插件开发与应用 1. 引言 如果你用过ComfyUI,肯定会被它那种节点拖拽、自由连接的工作流设计所吸引。它把AI图像生成的每一步都变成了可视化的模块,从加载模型到生成图片,整个过程清晰可控。但不知道你有没有…...

零门槛体验:VoxCPM-1.5-WEBUI三步部署教程,快速上手语音合成

零门槛体验:VoxCPM-1.5-WEBUI三步部署教程,快速上手语音合成 1. 为什么选择VoxCPM-1.5-WEBUI? 在当今内容创作爆炸的时代,语音合成技术正变得越来越重要。无论是制作短视频配音、有声读物,还是开发智能客服系统&…...

Windows安卓子系统(WSA)实用指南:3步快速部署与5大优化技巧

Windows安卓子系统(WSA)实用指南:3步快速部署与5大优化技巧 【免费下载链接】WSA Developer-related issues and feature requests for Windows Subsystem for Android 项目地址: https://gitcode.com/gh_mirrors/ws/WSA 你是否想在Windows 11电脑上无缝运行…...

2026年MySQL安装教程(超详细)

MYSQL下载及配置,一遍成功引言第一部分:下载教程第二部分:安装与配置第三部分:验证引言 大家好,我是菜程序,今天聊聊mysql的下载及配置,后端开发一定离不开数据库的支持,所以我便整…...

Hermes Agent 工具-周红伟

工具是扩展智能体能力的函数。它们被组织成逻辑上的工具集,可以在每个平台上启用或禁用。Hermes Agent 附带了一个广泛的内置工具注册表,涵盖网页搜索、浏览器自动化、终端执行、文件编辑、记忆、委托、RL 训练、消息投递、Home Assistant 等。可用工具工…...

「鸿蒙智能体实战记录 13」智能体上架提交与审核通过实现

📘 鸿蒙智能体实战记录 13 智能体上架提交与审核通过实现一、本篇目标 完成以下内容: 新建隐私协议完成隐私协议服务配置完成内容合规设置补充审核安全提示词进入智能体上架提交页面填写上架说明上传效果截图附件完成最终提交与验证二、新建隐私协议 进入…...

聊聊C语言那些事儿之概览

十分感谢前来阅读的读者和有兴趣学习c语言的朋友们,萌小编会和大家一起学习c语言。我们的口号是:学好,玩好,快乐就好!、 话说当年Dennis Ritchie在贝尔实验室做了一件开天盘古的大事件,发明了c语言,从此c语…...

Qwen3.5-9B快速部署方案:本地IP直连+防火墙端口开放实操

Qwen3.5-9B快速部署方案:本地IP直连防火墙端口开放实操 1. 项目概述 Qwen3.5-9B是一款拥有90亿参数的开源大语言模型,具备强大的逻辑推理、代码生成和多轮对话能力。该模型支持多模态理解(图文输入)和长上下文处理(最…...

FireRed-OCR Studio部署教程:Qwen3-VL工业级文档解析一键启动

FireRed-OCR Studio部署教程:Qwen3-VL工业级文档解析一键启动 1. 工具介绍 FireRed-OCR Studio是一款基于Qwen3-VL模型开发的工业级文档解析工具。它能将纸质文档、PDF截图等图像内容精准转换为结构化Markdown格式,特别擅长处理以下复杂内容&#xff1…...

JavaScript+WebGL可视化LingBot-Depth点云数据

JavaScriptWebGL可视化LingBot-Depth点云数据 1. 引言 想象一下,你手里有一个深度相机,它能捕捉到周围环境的3D信息,但原始数据往往充满了噪声和缺失区域。这就是LingBot-Depth发挥作用的地方——它能将不完整、有噪声的深度数据转换为高质…...

AI元人文:意义行为原生论的发生学阐明与伦理中间件建构

AI元人文:意义行为原生论的发生学阐明与伦理中间件建构摘要:本文旨在系统阐述一种名为“意义行为原生论”的理论框架,其核心结构为“舍得结构”。该理论拒斥将意义视为某种先验实体或行为结果的附属品,而是将其锚定于D&#xff08…...

Qwen3-TTS快速入门:上传15秒语音,一键生成你的专属AI配音

Qwen3-TTS快速入门:上传15秒语音,一键生成你的专属AI配音 1. 为什么选择Qwen3-TTS进行语音克隆 想象一下这样的场景:你需要为视频教程配音,但自己录音总是卡壳;或者想给海外客户发语音邮件,却苦于外语发音…...

Vision Transformers与CNN-Transformer混合架构:演进、融合与应用全景

1. Vision Transformers的崛起与挑战 2017年Transformer架构在NLP领域大放异彩后,计算机视觉研究者开始思考:能否用同样的方式处理图像?2020年Dosovitskiy等人提出的Vision Transformer(ViT)给出了肯定答案。与CNN逐层…...

多进程-生产者消费者C++实现

条件变量通常与互斥锁配合使用,用于线程之间的通信和同步。它允许线程在某个条件满足之前等待,当条件满足时,其他线程可以通知等待的线程继续执行。例如,在生产者 - 消费者模型中,生产者线程生产数据后,通过…...

冲刺待办列表管理化技术任务分解与估算

冲刺待办列表管理化技术任务分解与估算:高效协作的核心 在快节奏的软件开发中,冲刺待办列表(Sprint Backlog)是敏捷团队实现目标的关键工具。通过将复杂任务拆解为可执行单元并合理估算工作量,团队能提升交付效率与质…...