ConcurrentHashMap 1.7与1.8源码对比:从Segment到CAS+synchronized的并发演进
做Java开发的朋友多少都被ConcurrentHashMap“教育”过。面试要问源码分析要讲线上排查高并发问题也绕不开它。而只要一聊到源码1.7和1.8这两个版本的区别就是永远躲不开的坎一个是Segment分段锁一个是CAS加synchronized一个锁粒度粗但模型简单一个锁粒度细但实现极其精巧。我最初在1.7时代看这段源码时觉得分段锁的设计已经挺聪明了至少比HashTable全表上锁强太多。后来1.8出来看到它直接把Segment整个砍掉换成“CAS空桶插入 synchronized锁桶头节点”的组合说实话第一感觉是这也太敢改了。但真正把两版源码放在一起逐行对比之后才理解1.8这一步不是炫技而是彻底解决了1.7在高并发下的几个结构性痛点。这篇文章我打算从设计思路、数据结构、读写流程、计数方式、扩容机制几个维度把1.7和1.8的差异一次性讲透最后再补上我在实际项目里从1.7迁移到1.8后踩过的坑。无论你是准备面试还是想搞清楚线上该用哪个版本这篇都适合从头读到尾。1. 设计思路的演变从“分段锁银行柜台”到“精细到每个窗口”1.1 1.7的Segment分段锁解决的是HashTable的全表锁问题在1.7以及更早的版本里Java官方提供的线程安全Map只有HashTable它的实现很粗暴所有方法上加一把大锁也就是把整个表锁住。并发的读和读之间都会被卡住更别说读写混合的场景。在低并发下问题不大一旦并发量上来这把锁就成了系统瓶颈。ConcurrentHashMap 1.7引入了一个非常经典的思路既然一把锁锁全表太浪费那就把表拆成很多段每段一把锁。这个思路就是银行办理业务时的柜台模型——大厅里不是只有一个窗口让所有人都挤在一起而是开多个窗口不同的客户去不同的窗口排队窗口之间互不影响。这就是Segment。默认情况下ConcurrentHashMap 1.7内部有一个Segment数组默认长度是16也就是说并发度默认是16。每次put的时候先用哈希值定位到某一个Segment然后只锁这个Segment其他Segment的读写完全不受影响。理论上支持16个线程同时写不同的Segment这个并发度在当时已经非常可观了。每个Segment本身是一个小型HashMap内部维护一个HashEntry数组HashEntry里面是key、hash、value以及next指针。发生哈希冲突的时候就拉出一个链表遍历链表完成插入或更新。这个结构和1.7的HashMap几乎一样只不过所有操作都被Segment加了一层锁保护。1.2 1.8为什么敢推翻Segment锁粒度进一步细化才是正解1.8的改动表面上把Segment去掉了实际上是保留分段思想的同时把锁粒度从“段”降到了“桶”。所谓桶就是table数组里的每个槽位。每一个桶都是一把独立的很小很小的“锁”竞争的线程只有落到同一个桶上才会被阻塞落到不同桶的线程不管多少都能并行推进。那为什么不继续用Segment而是改用synchronized有以下几个原因。第一JDK团队在1.6之后对内置锁synchronized做了大量优化引入了偏向锁、轻量级锁、重量级锁的升级路径synchronized在低竞争场景下开销已经非常小甚至低于ReentrantLock。用内置锁处理“锁单个桶头节点”这种细粒度操作性价比很高。第二Segment数量是固定的默认16之后就不能再变。这意味着就算你把table扩容到几千个桶单个Segment内部管着的仍然是几十甚至上百个桶锁竞争还是集中在那16把锁上。换个角度说1.7的并发度上限被写死在代码里了扩容解决的是哈希冲突问题却解决不了高并发下的锁瓶颈问题。第三1.8还把数据结构升级了单个桶里的链表如果过长会自动转成红黑树。这样即便哈希冲突再严重桶内的查询复杂度也能从O(n)降到O(log n)。链表里的节点变多了锁一个桶的时间也就那么短锁竞争压力自然小很多。锁粒度从Segment级别降到桶级别是整个设计思路最核心的变化后续所有代码差异都是围绕这个点展开的。1.3 锁粒度细化以后并发度到底提升了多少很多人只记得“1.8锁更细了”但没有量化概念。锁粒度等于什么等于你允许多少个线程同时做写操作。1.7的并发度 Segment数组长度默认16固定不可变。1.8的并发度 table数组的当前长度也就是桶的个数这个值会随着扩容变大初始默认16扩容后翻倍到32、64、128……而且就算某个桶在写其他桶的线程依然可以写。举个例子如果table长度是64极端情况下可以允许64个线程分别写不同桶只要它们落在不同的桶上就完全无冲突。这个提升是数量级的。当然实际并发写入未必能打满桶数那么多因为哈希分布不一定完全均匀但至少代码层面已经不再人为限制并发度上限了。另外还有一个容易被忽略的点内存占用。1.7的Segment本身就是对象一个Segment内部又有HashEntry数组存在大量额外对象头引用1.8把Segment去掉直接用table数组对象结构更扁平GC压力也小一些。线上大数据量场景下这一点体感会比较明显。2. 数据结构与寻址方式两版的内存布局完全不是一回事2.1 1.7的内存布局多了一层Segment包装1.7的整体结构可以画成这样最外层是Segment数组每个Segment里面是一个HashEntry数组每个HashEntry数组的槽位上是一个链表。所以一次put要经历两级寻址第一步根据key的hash值经过一次再哈希rehash降低哈希冲突概率然后通过“hash右移segmentShift再与segmentMask做与运算”定位到具体的Segment。第二步进入Segment内部后用另一个哈希定位到Segment内部table数组的某个槽位。这里Segment数组的长度参数很重要默认16个Segment对应segmentShift是28segmentMask是15。如果你传入自定义的concurrencyLevel最终Segment数组长度也会向上取整到2的幂次。HashEntry节点的next指针在1.7中是非volatile的但value是volatile的。写的时候通过加锁来保证安全读的时候依赖volatile保证可见性。这就是那个经典的无锁读思路写的时候锁住读的时候不加锁靠volatile读最新的value。2.2 1.8的内存布局去掉Segment扁平化tag数组1.8是真正的扁平化结构一个大的Node数组每个Node节点要么是普通链表节点要么是红黑树的树节点。没有包装层没有Segment。Node节点的hash和value字段都是volatile的next也是volatile。这一点比1.7更进一步链表节点的结构信息在并发读写下的可见性得到了完整保障。当某个桶的链表长度达到8TREEIFY_THRESHOLD时代码不会立即转成红黑树还有一个前置条件当前table数组长度必须大于等于64MIN_TREEIFY_CAPACITY。如果还没到64优先扩容而不是树化。这是因为数组短的时候大家都挤在很少的桶里单纯树化解决不了整体哈希分布差的问题扩容才是更合理的方案。红黑树结构的实际载体是TreeBin。TreeBin本身不存储key和value它内部持有红黑树的根节点并且维护读写锁状态。读线程在红黑树上查找时不需要加锁写线程在维护树结构时通过CAS加锁。这个设计相当精妙读多写少时读操作几乎无阻塞。2.3 哈希寻址方式的变化两次定位变成一次定位1.7里为了保证哈希值够“散”用了一个再哈希函数对原始hashCode做二次扰动。这个再哈希逻辑我记了很多年它是一个类Wang/Jenkins的移位混合算法把高位信息和低位信息充分打乱从而降低哈希冲突概率。代价是要多算一轮位运算在极端热路径上CPU开销略高。1.8把再哈希函数换成了一个更轻量的扰动直接拿key.hashCode()异或上它右移16位的结果。也就是static final int spread(int h) { return (h ^ (h 16)) HASH_BITS; }这个扰动方式把高16位的信息混合到低16位里因为table数组的下标计算用的是 (n - 1) hash如果hash值只有低16位在起作用那高16位的信息就浪费了。异或一下右移16位可以让高16位也参与下标计算。1.8能放心简化再哈希函数很重要的原因是底下有红黑树兜底即使冲突变多了链表过长会被树化查询效率不会退化到无法接受。这是一个“源头降载加末端兜底”的组合拳。3. 核心操作逐项对比put、get、size、扩容3.1 put操作从锁Segment到CAS加synchronized1.7的put逻辑是这样的先定位到Segment然后调用Segment的put方法这个方法内部会先尝试tryLock加锁成功就直接操作如果tryLock失败说明这个Segment正在被其他线程写当前线程会进入scanAndLockForPut逻辑一边自旋一边等锁自旋超过阈值就挂起直到拿到锁为止。加锁成功之后会检查当前Segment内部的元素个数是否超过阈值。如果超过就在锁内进行rehash扩容然后执行头插法把新的HashEntry插入链表头部。头插法是1.7的一个特点新节点插在链表头部时间复杂度是O(1)不需要遍历到链表尾部。1.8的put流程是一个死循环里反复尝试的过程我用伪代码和文字结合的方式说一下关键步骤线程进入putVal之后先检查table是否为null如果为null就调用initTable初始化数组。然后计算下标检查这个位置是否为空。如果为空就通过CAS操作把新节点直接放进去这一步完全不涉及加锁是最理想的路径。如果这个桶不为空说明发生了哈希冲突这时候才走加锁逻辑。加锁方式变成了内置synchronized锁对象是这个桶的头节点。拿到锁之后再次检查头节点是否还是原来的节点防止期间被其他线程改了结构。然后分两种情况处理如果头节点是普通链表节点就遍历链表找到相同key就替换value没找到就尾插法加在链表后面如果头节点是TreeBin说明这个桶已经树化走红黑树的插入逻辑。插入完成之后检查链表长度是否达到8达到就尝试转为红黑树。整个过程结束后如果链表长度变化了还需要维护节点修改计数相关的字段。关键差异我列个表更直观对比项1.71.8锁机制Segment内部ReentrantLockCAS synchronized锁对象Segment对象桶头节点无锁路径无必须加锁桶为空时CAS直接插入链表插入方式头插法尾插法树化不支持链表长度8且数组长度64这里有一个很多人忽略的细节1.8的put在遇到桶头节点是ForwardingNode也就是这个桶正在被扩容迁移时当前线程不会傻等而是主动帮助扩容。这个后面在扩容部分具体讲。3.2 get操作两版都是无锁读但可见性保障有差别ConcurrentHashMap的读操作一直不做加锁这是它性能的核心来源之一。get的核心思路是定位到桶遍历链表或红黑树找到hash值相等且key相等的节点然后读取volatile的value字段并返回。1.7的get有两级定位先定位Segment再定位Segment内部table的桶然后遍历链表。因为value字段是volatile的所以即使写入线程还没退出临界区读线程也能读到最新写入的值。1.8的get定位更直接tabAt方法通过Unsafe的getObjectVolatile读取table数组的指定位置保证读到的table最新、每个桶的头节点引用最新。然后检查头节点的hash值hash 0 说明这个节点有特殊含义比如ForwardingNode表示正在扩容TreeBin表示这个桶已经是红黑树ReservationNode表示占位。如果是ForwardingNode说明table正在扩容当前线程会跳到nextTable去继续查找而不是原地等待所以扩容期间get不会被卡住。如果是TreeBin走红黑树查找逻辑。如果是普通Node就遍历链表。1.8的get也不需要加锁因为它依靠的是volatile的读语义以及Node节点内部hash和next字段的volatile可见性。一个值得说的细节是1.8在get的链表遍历中判断key相等用的逻辑是先比较hash再比较key本身因为hash相等是进入下一步比较的必要条件可以过滤大量错误节点。这个在1.7中也有类似逻辑但1.8因为hash负数有特殊含义判断流程更谨慎。3.3 size计数机制从“锁全表统计”到“近似值算账”1.7的size操作最让我觉得“重”先不加锁地统计两次所有Segment的modCount之和如果前后两次相等说明统计期间没有写操作发生那么这次的结果是准确的如果发现两次不等说明有写操作这时候就把所有Segment锁一遍逐个统计count再求和。如果并发量一高size容易触发全表锁开销很大。1.8彻底改掉了这个思路。它维护了一个baseCount每次写操作完成后线程会通过CAS尝试更新baseCount。如果CAS失败说明有竞争就让这些竞争线程把计数写到一组CounterCell数组里每个线程分散到不同的CounterCell上避免大家一起抢同一个变量。计算size的时候直接把baseCount和所有CounterCell的值加起来。这个求和过程没有加锁所以它理论上不是一个绝对精确的实时值而是一个趋于准确的值。JDK源码里甚至加了sumCount的注释强调这一点。为什么1.8敢这么做因为很多用到size的场景本身就不需要强一致的精确值。比如做一个统计报表、判断是否需要清理缓存误差几百万分之一根本不影响业务。如果有人真的需要精确大小应该在业务层自己加锁或者用其他机制。3.4 扩容机制从“各扩各的段”到“大家一起搬”扩容是1.7和1.8差异最大的一块也是1.8实现最复杂的地方。1.7扩容发生在Segment内部。当某个Segment内元素数量超过阈值就在该Segment加锁状态下把内部table数组扩容到原来的两倍然后重新哈希当前Segment中的所有节点到新数组。因为锁是粒度为Segment级别的所以扩容时其他Segment不受影响可以继续工作。但这种做法的问题是扩容成本完全由触发扩容的那个线程独自承担。如果某个Segment内的数据特别多rehash迁移节点就需要很长时间其他想写这个Segment的线程只能等着这就是所谓的“一段扩容同段阻塞”。1.8的扩容变成了一场“集体大迁移”。触发扩容后会创建一个新的table数组长度变为原来的两倍然后多个线程可以一起参与搬迁。每次搬迁的最小单位是一个区间这个区间叫stride代码里会根据CPU核数计算每个线程应该负责的桶区间大小。搬迁过程中已经处理完的桶会放一个ForwardingNode占位。其他线程无论是put还是get遇到ForwardingNode就知道这个桶已经被搬走了。put线程看到它会主动帮忙继续扩容get线程看到它会直接去新table里查找。这种设计让扩容不再是单点阻塞而是充分利用并发性能。但这个多线程协作扩容也带来了极复杂的并发控制包括扩容进度标记、nextTable共享、节点迁移顺序保证每一步都可能踩并发坑。这也是为什么1.8的ConcurrentHashMap源码行数比1.7翻了近两倍。4. 面试与实战中最常遇到的问题排查4.1 并发度到底是多少别再记错的固定答案1.7的并发度由Segment数组长度决定默认16。你可以在构造时传入concurrencyLevel但ConcurrentHashMap不会因为table扩容而增加Segment数量所以并发度写死在那里。这个“写死”才是1.7的槽点。1.8没有并发度的概念。理论上并行写能力由table数组长度决定而且会随扩容提升。所以如果面试官问“1.8的并发度是多少”标准答案不是数字而是“桶的数量动态变化”。4.2 红黑树什么时候触发什么时候退化链转树的条件是两个链表长度大于等于8同时table数组长度大于等于64。为什么阈值是8因为源码注释里有一个泊松分布说明——在随机哈希码下链表长度达到8的概率已经低到千万分之几。选8是为了让树化极少发生避免红黑树本身的维护成本白白吃掉性能。树转链的条件是拆分红黑树时红黑树节点小于等于6UNTREEIFY_THRESHOLD。这里特意保留了“8变树、6变链”的差值避免阈值附近频繁来回切换造成抖动。实际工作中如果发现某个场景频繁触发树化不要只盯着阈值更可能的原因是哈希函数分布不均匀。检查一下你的key有没有重写hashCode比如很多自定义对象没有合理重写hashCode导致大量对象hash值低位相同全部挤到同几个桶里。4.3 为什么ConcurrentHashMap不允许key或value为null这个问题属于“看着简单背后深刻”的类型。HashMap允许一个null key和多个null value但ConcurrentHashMap从1.7到1.8都不允许。官方也做过解释最主流的说法是如果允许null value那么get返回null时无法区分“key不存在”和“key存在但是value是null”这两种情况。HashMap设计得比较粗糙把选择权交给用户ConcurrentHashMap是并发容器如果把判断逻辑建立在“返回null代表不存在”这个假设上在多线程环境里很容易引入歧义和误判。如果你在源码里翻过putVal会发现代码里有一行明确判断如果key或者value为null直接抛出NullPointerException。这也是一个容易在线上踩的坑——从HashMap迁移到ConcurrentHashMap时原来可以put进去的null值现在直接异常。4.4 弱一致性的迭代器到底意味着什么无论1.7还是1.8ConcurrentHashMap的迭代器都是弱一致性的。意思是创建迭代器之后如果在迭代过程中其他线程修改了map迭代器不会抛ConcurrentModificationException也不会保证一定读到最新数据它可能读到修改前、修改后的数据或者跳过某些节点。这和HashMap的fail-fast迭代器是两个极端。HashMap遇到结构修改直接抛异常很多老项目定期全量遍历HashMap和ConcurrentHashMap时的行为完全不同。如果你在迭代过程中依赖“读取结果必须和某一时刻的全体快照一致”那应该先用putAll构建快照或者干脆做全局锁。4.5 两版关键差异速查表维度1.71.8锁机制Segment继承ReentrantLockCAS synchronized锁粒度默认16个Segmenttable单个桶并发度固定16可配置但固定后不可变随table扩容动态提升数据结构Segment HashEntry 链表Node 链表 红黑树哈希定位再哈希 两级定位spread扰动 一次定位put无锁路径无桶为空时CAS直接插入链表插入头插尾插sizemodCount统计失败锁全表baseCount CounterCell近似值扩容单Segment内独自扩容多线程协作ForwardingNode辅助迭代器弱一致弱一致5. 从1.7迁移到1.8时我在实际项目里踩过的坑5.1 第一个坑size不准但很多人拿它判断是否清理缓存以前用1.7时我们有个全局注册表上限是十万条数据超过就淘汰一部分不活跃的key。旧代码里用size()判断容量这个值在1.7里相对准确因为它在检测到变化后会锁全表重统计代价是慢。迁移到1.8之后线上有个监控报警显示注册表大小浮动异常大偶尔比实际业务量差很多。查下来原因很简单size变成了近似值在写入非常频繁的窗口期CounterCell还没被及时统计到baseCount里去测出来的数总比真实值少一些。后来调整成“不一定精确但必须持续稳定”的淘汰策略之后这个问题就消失了。我的建议是不要用ConcurrentHashMap的size做精确的业务判断如果非要判断可以做一个定时任务定期全量扫描或者自己维护一个AtomicLong计数器。5.2 第二个坑热点Key造成单桶锁膨胀性能瞬间下滑1.8把锁粒度降到桶但理论上仍然可能出现极端情况大量并发写同一个key。比如业务里有一个全局配置项所有机器启动时都去更新这个key几十个线程同时写锁全砸到那一个桶上synchronized锁升级之后整个系统在这一瞬间的表现和全表锁没有区别。这种场景下代码结构再怎么优化也救不了锁膨胀应该从业务侧分流。后来我们的方案是给每个key增加一个随机后缀分片比如把配置项拆成64个分片写入时随机选分片读取时遍历全部分片合并结果。写并发被稀释到64个桶上性能问题自然消失。5.3 第三个坑自定义对象作为keyhashCode写得太敷衍1.8的spread函数虽然已经足够好但它管不了key自己的hashCode质量。有一次线上出现某个桶的链表长度长时间维持在十几触发了树化但树化之后读写效率还是不高。排查之后发现那个key对象是自定义的结构体hashCode只返回了其中一个字段的值大量不同对象的hashCode完全相同直接导致spread之后低16位全一样全部落到同一个桶。这从侧面说明再好的并发容器也架不住业务方把key的哈希分布搞砸。解决办法是重写hashCode或者干脆换一个散列性更好的key类型。5.4 最后一个心得从1.7到1.8的升级本质是并发控制思路从“锁竞争更少”变成了“锁存在时间更短、粒度更细”再加上数据结构的配合让读写路径都尽可能无锁化。源码里那些精巧的位运算和CAS逻辑不是用来炫技的而是每一个设计都在为解决真实的高并发痛点服务。现在我再看这两版代码反而更推荐大家不要死记硬背结论而是亲手打开源码跟着put和get两条路径走一遍。你会发现1.8里那些看似随意的数值从8、64到6每一个都有来源、有计算、有取舍。真正理解了这些设计背后的权衡你在面试里不管被问到多刁钻的问题都能从根上给出让人信服的回答。

相关新闻

WinForm文本编辑器源码解析与高级功能扩展实战指南

WinForm文本编辑器源码解析与高级功能扩展实战指南

简介:这份C#文本/代码编辑器专题文档,面向具备一定C#基础、想从零构建或二次开发编辑器的研发人员,解决从界面搭建、文件操作到功能扩展各环节的关键问题。文档先讲解基于WinForm与RichTextBox的基础实现,包括字体加粗/斜体/下划线…

2026/10/15 7:37:59 阅读更多 →
微信小程序投票系统怎么从零搭建?以活动评选为例的完整流程

微信小程序投票系统怎么从零搭建?以活动评选为例的完整流程

从技术视角看,在微信生态里"创建一个投票活动",本质是配置一套活动数据结构 报名审核状态机 防刷策略 数据导出能力。本文以「活动评选」场景为例,梳理从创建到发布的完整流程,并以投票帮小程序作为落地实践案例。 …

2026/10/15 7:37:59 阅读更多 →
T型三电平逆变器VSG自适应控制与并离网切换优化

T型三电平逆变器VSG自适应控制与并离网切换优化

1. 项目背景与核心价值电力电子变换器在新能源发电系统中扮演着关键角色,而T型三电平逆变器因其高效率、低损耗的特性,在中大功率场合得到广泛应用。传统逆变器控制策略在并离网切换过程中常面临暂态冲击大、同步性能差等问题,这正是虚拟同步…

2026/10/15 7:37:59 阅读更多 →

最新新闻

Spring Boot实战:高校学生电子履历管理系统设计与实现

Spring Boot实战:高校学生电子履历管理系统设计与实现

折腾完这套高校学生电子履历管理平台,最大的感觉就是:简历系统看着不起眼,实际做起来比想象中复杂得多。这个项目虽然以毕业设计的形式展开,但功能覆盖了学生端、企业端、管理员端的完整闭环,既要把 Java Web 环境下常…

2026/10/15 8:18:28 阅读更多 →
SpringBoot+Android电影推荐系统开发实战

SpringBoot+Android电影推荐系统开发实战

1. 项目概述这个基于SpringBoot和Android的电影推荐系统,是我去年为一个本地影院开发的商业项目。核心目标是解决用户在海量影片中"选择困难"的问题,通过个性化算法为用户推荐可能感兴趣的电影。系统采用B/S架构,后台用SpringBoot实…

2026/10/15 8:18:28 阅读更多 →
【股票交易】第 19 章 成本结构、经营杠杆与规模效应

【股票交易】第 19 章 成本结构、经营杠杆与规模效应

回到目录 文章目录 19.1 成本是经营选择的结果 经营选择先决定成本结构 固定与变动取决于时间和业务范围 灵活性、控制与承诺之间的取舍 19.2 收入变化怎样放大为利润变化 成本不能同步调整时,利润会放大波动 宝洁收入增加以后,利润怎样变化 经营杠杆与盈亏平衡的计算条件 用…

2026/10/15 8:18:28 阅读更多 →
中国开源年会侧记:社区治理、AI开源与生态转型

中国开源年会侧记:社区治理、AI开源与生态转型

今年的开源年会,我不仅去了,还提前半小时到场,结果在签到处排了快二十分钟。这种久违的拥挤感,说实话,反而让人心里踏实。作为常年泡在开源社区里的开发者,我亲眼看着国内开源活动从偏技术沙龙变成全产业链…

2026/10/15 8:18:28 阅读更多 →
PolyMetatron 系统提示词全解:用 FOL、CNF 与 HOL 构建逻辑问题求解与知识综合管道

PolyMetatron 系统提示词全解:用 FOL、CNF 与 HOL 构建逻辑问题求解与知识综合管道

提示工程AI 安全治理 【免费下载链接】TheBigPromptLibrary A collection of prompts, system prompts and LLM instructions 项目地址: https://gitcode.com/gh_mirrors/th/TheBigPromptLibrary 点击查看 免费下载 导读 本文以 TheBigPromptLibrary 仓库收录的 P…

2026/10/15 8:18:28 阅读更多 →
WebSocket全双工真相:不是同时收发,而是帧级流控

WebSocket全双工真相:不是同时收发,而是帧级流控

1. 为什么全双工不是“同时收发”那么简单——从一个被反复误解的协议说起你肯定用过实时聊天、在线协作文档、股票行情推送,甚至网页版远程桌面。这些功能背后,几乎都站着同一个沉默的功臣:WebSocket。但很多人一听到“全双工”,…

2026/10/15 8:17:28 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →