Java 并发编程笔记
juc.xmind 写在前面的话 并发编程最早的实践都在操作系统里。 管程 理论和实践之间是有鸿沟的,要弥合这种鸿沟,通常需要我们去学习别人的实践。比如并发的标准设计思想来自于操作系统里的管程(monitor),我们应当学习管程,进而了解标准的并发模型-管理共享变量和线程(并发任务)间通信的基本理论模型。 MESA 模型 JAVA 采用 MESA 模型: 互斥(Mutual Exclusion):通过锁机制保证同一时刻只有一个线程能进入管程内部执行。 同步(Synchronization):利用条件变量(Condition Variable)实现线程间的等待与唤醒。 Signal and Continue: 当线程发出通知(signal/notify)时,它继续持有锁并运行,而被唤醒的线程仅仅是进入就绪队列,并不立即抢占 CPU。 必须使用 While 循环: 由于线程被唤醒后不一定立即执行,当它重新获得锁时,环境条件可能已发生变化,因此必须在一个 while 循环中重新检查等待条件(while (condition) { wait(); })。 为什么用 se...
常见故障整理
手写 sql if 条件的字段为空则不应该拼接条件,是一个很容易被忽略的编程错误。如果线上发生了这个问题,则可能导致数据同步出错。 极度危险的错误 元素内容必须由格式正确的字符数据或标记组成,这通常是因为>``<``>=``<=类的标签没有经过转义。 防止手写 sql 被注入 所有 condition 用()圈起来。单独在 condition 里面拼装。用 and 来连接这些 condition。 在最外围使用随机化的()来包裹整个 where 的条件,防止有人猜到()的层次。 mybatis 的替换难点 #{} 是预编译处理,${} 是直接替换。直接替换会有 sql 注入的风险。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991...
线程池详解:ForkJoinPool
本文由 qwen 生成。 前言:分治并行的诞生 “While thread pools are effective for many concurrent programming tasks, they do not scale well for programs that are structured as recursively parallel computations.” ForkJoinPool 不是为了"并行"而设计,而是专门为分治并行(Divide-and-Conquer Parallelism)这一特定模式量身定制。分治算法(如快速排序、归并排序、树遍历)具有独特的执行模式: 任务天然形成树状结构 父任务派生子任务后需要等待结果 子任务之间通常无依赖关系 计算密集,无I/O阻塞 理解分治算法的执行特性,是理解 ForkJoinPool 设计的关键。传统线程池在处理这类任务时遇到根本性挑战,ForkJoinPool 正是为解决这些挑战而诞生。 1. 核心数据结构:ForkJoinPool的基石 1.1 ForkJoinPool:去中心...
LLM 代码修改传递指令模板
系统指令 12345678910111213141516171819202122232425你是一个专业的编程助手。在整个对话过程中,你需要随时准备生成一个完整的实现指南(输出为一个md格式的文件),让另一个 LLM 能够在原始文件上重新实现你所做的所有修改。## 核心要求:1. **记录所有修改**:跟踪每一个代码变更、新增功能、配置修改等;除了聊天上下文里的代码变更以外,还要读取当前的 git diff 输出2. **生成传递文档**:当用户要求时,立即生成包含以下内容的 Markdown 文档: - 完整的上下文说明(项目背景、功能需求) - 详细的实现步骤(按顺序列出所有修改) - 准确的代码变更(包含完整的 Git Diff 格式) - 技术细节和注意事项 - 验证方法和测试要点## 文档结构模板:- **概述**:功能描述和业务价值- **项目信息**:仓库、文件路径、相关依赖- **实现步骤**:逐步的修改指导- **代码变更**:完整的 Git Diff 或代码片段- **技术要点**:关键实现细节和最佳实践- **注意事项**:配置要求、依赖...
分布式事务
问题定义 对经典的电商场景而言:下单是个插入操作,扣减金额和库存是个更新操作,操作的模型不同,如果进行分布式的服务拆分,则可能无法在一个本地事务里操作几个模型,涉及跨库事务。 CAP 定义 根据 Eric Brewer 提出的 CAP 理论: Consistency:All Nodes see the same data at the same time。所有节点看到同一份最新数据(线性一致性)。 Availability:Reads and writes always succeed。非故障节点必须在合理时间内响应。 Partition tolerance:System continues to operate despite arbitrary message loss or failure of part of the system。网络分区时系统继续运行。 由此诞生三种设计约束和取舍方向: CA:放弃P,仅适用于单点系统,非分布式,如 MySQL主从同步。 AP:放弃强一致性,保证高可用。Cassandra,DynamoDB。Gossip协议可实现最终一致性。 CP...
系统设计
System Design 总结 面试前你需要了解的16个系统设计知识 《搞定系统设计:面试敲开大厂的门》 从 0 到 100 万用户的扩展 数据库的选择 先把服务器里计算和存储的服务器分离开来。 在如下情况下,可以考虑 NoSQL: 低延迟。 非结构化非关系型。 只需要序列化格式-或者对序列化格式友好。 需要存储海量数据。 《你到底用 NoSQL 来做什么?》 scale up vs scale out 纵向扩展的缺点: 有硬性限制。 没有冗余。 横向扩展对大型应用更合适。 负载均衡器 failover 基于负载均衡器就够了,有了 LB 我们才能装多台服务器。 数据库复制 从换主很容易丢失数据,先要恢复脚本才能实现对数据的合法写和读。 缓存 读写模式 我们常用的缓存策略叫 Cache-Aside Cache,完整介绍在《Caching Strategies and How to Choose the Right One》。 如果是应用自己加在缓存,就是 cache aside;如果缓存自己带有 load 方法-比如 guava 的 loader 实现,则意味着 ...
《编程之美》
序言 下水道井盖为什么是圆的 “下水道井盖是圆的,因为圆形不会掉进井口,而且圆形具有均匀分布压力的优势。” 一个屋子有一个门(门是关闭的)和3盏电灯。屋外有3个开关,分别与这3盏灯相连。你可以随意操纵这些开关,可一旦你将门打开,就不能变换开关了。确定每个开关具体管哪盏灯? 答:将一盏灯开一段时间,再关掉,在剩余2盏灯里随机开一盏,进屋去看,发热的灯对应第一个碰的开关,亮着的灯对应开关开着的开关,灭的灯对应没碰过的开关。 游戏之乐 如何写一个程序让 cpu 占用率保持在 50%? 不要用 if-else 来解决,要把比例转成不同的 worktime。 解法的精确与否其实取决于“多久时间内测度一次已占用的时间”和“睡眠”两类 api 的精度。 bash 版本 12345678910111213#!/bin/bash# 精简版CPU负载控制器L=${1:-50} # 默认50%[ $L -lt 0 ] || [ $L -gt 100 ] && { echo "用法: $0 [0-100]"; exit 1; }...
Grokking the System Design
设计一个电梯系统 项目链接 myElevator 思路 1. 这道题考察候选人的什么知识? 面试官不是真的想让候选人造一台电梯,而是想通过这个问题评估候选人的综合能力,主要包括: 需求分析与沟通能力:这是最重要的一点。一个优秀的工程师在动手前,会先问问题,明确需求和边界。直接埋头开始写代码的候选人通常会失分。 面向对象设计 (OOD) 能力:这个问题是考察OOD的绝佳场景。如何将现实世界的实体(电梯、楼层、按钮、乘客)抽象成清晰、低耦合的对象和类? 算法与数据结构:电梯调度策略是整个系统的核心,这直接考察候选人的算法设计能力。如何选择合适的数据结构来存储和处理请求,以实现高效的调度? 状态机建模能力:电梯的运行本身就是一个状态机(静止、上升、下降、开门、关门等)。能否清晰地定义这些状态以及它们之间的转换条件,是衡量逻辑思维严谨性的关键。 并发与同步问题:多部电梯、多个乘客请求,这些都是并发场景。候选人是否能意识到可能存在的竞态条件(Race Condition)和资源同步问题? 系统扩展性 (Scalability):设计是只针对一台电梯,还是一个拥有多台电梯的系统?如何将单...
我与 AI 的问答
HTTP 请求体只读一次之谜:Go 与 Java 的应对之道 作为一名后端开发者,你几乎肯定会遇到这个经典场景:为了实现日志记录、签名验证或请求重放,你需要在中间件(Middleware/Filter)中读取 HTTP 请求体(Request Body)。然而,当你将请求传递给下一个处理程序时,却发现 Body 变成了空的!程序抛出错误,逻辑中断。 这不是一个 Bug,而是网络 I/O 流处理的一个基本特性。本文将深入探讨这个问题的根源,并详细对比 Go 和 Java 在处理“可重复读 Body”这一问题上的不同解决方案,揭示其背后截然不同的设计哲学。 第一部分:问题的根源——流的“阅后即焚”本质 为什么 HTTP 请求体默认只能读取一次? 我们可以把请求体想象成一条从网络连接中实时流淌过来的数据河,而不是一块已经完整存放在硬盘上的文件。 效率至上:当服务器收到一个 HTTP 请求时,特别是像文件上传这样带有巨大请求体的请求,如果需要先把整个几 GB 大小的文件都读到内存里才能开始处理,那将是极其低效且消耗内存的。在繁忙的服务器上,这会轻易导致内存溢出(Out of Memor...
Redis 的神奇用例
二级评论区 在现代 Web 应用中,评论系统是用户互动的核心功能之一。一个设计良好的评论系统不仅要能处理大量的读写请求,还需要支持诸如“回复评论”这样的嵌套结构(通常称为二级评论或评论回复)。Redis,作为一个高性能的内存数据库,凭借其丰富的数据结构,非常适合用来构建这样的系统。 本文将探讨如何利用 Redis 的 Hashes(哈希) 和 Sorted Sets(有序集合) 来设计和实现一个高效、可扩展的二级评论系统。 核心设计理念 我们设计的核心思想可以概括为两点: 实体存储: 使用 Redis Hash 来存储每条评论(包括一级评论和二级回复)的具体内容-不存博客的具体内容。 关系与排序: 使用 Redis Sorted Set 来维护评论之间的父子关系,并利用其天然的排序能力(基于 Score)来管理评论和回复的顺序(例如按时间倒序)。 数据结构详解 存储评论/回复内容 (Hash) 我们将每条评论或回复的实际数据存储在一个 Hash 中。 Key: comment:{unique_comment_id} (例如 comment:1001, comment:10...










