Java Semaphore实战:从底层原理到接口限流与连接池控制
Semaphore 给我的感觉就是一个放在方法入口处的通道闸机——你提前设置好“同时能放进去多少人”放进去之后后面的人就得在门外等着等里面的人出来一个再放一个进去。这玩意儿在 Java 并发编程里已经存在了二十多年可靠、轻量、用途极广但很多 Java 开发并没有真正把它用明白要么当摆设写了几行没人调用的代码要么就因为一个参数错误在生产环境放了烟花。这一篇就是围绕它写的实战笔记。我会从信号量的底层原理拆起把acquire、release、tryAcquire这些方法的差异性讲透然后通过几个直接可以搬走的案例接口限流、数据库连接限制、全局限流改进型带你把“流量阀门”这四个字从口号变成真正能扛压的代码。适合所有写 Java 的开发者尤其是手里管着线上接口、天天担心被突发流量打爆的同学。1. 内容整体设计与思路拆解1.1 为什么“锁”解决不了所有并发问题很多人的第一反应是并发太猛加锁啊。文件写入加锁、共享变量加锁、数据库更新加锁锁确实能保护共享数据的正确性但它的“串行化”思想在限流场景下是灾难。想象你有一个下单接口底层依赖数据库连接池假设最多 10 个连接。你用 ReentrantLock 把整个下单逻辑锁住同一时刻只有一个线程能进数据库其他 999 个线程全部阻塞等待。这样确实保证了不超连接数但代价是吞吐量几乎成了 1系统的响应时间表惨不忍睹。锁解决的是“同一时刻只有一个线程访问某资源”而信号量解决的是“同一时刻只允许 N 个线程访问一组资源”。这两者最大的区别在于“许可数”锁的许可数是 1信号量的许可数是 N。当你面对的是一个共享资源池、一组可复用连接、一段需要限流的业务逻辑时信号量的建模更加自然也不会把系统的并发能力压到 1。这个思路我在多个项目里验证过接口 QPS 高并发的场景用 Semaphore 做限流配合合理的超时时间和阈值系统稳定性提升非常明显而如果当时我用一把大锁去锁整个处理链路可能并发一上来就直接雪崩了。1.2 流量阀门的“N 许可”模型信号量的设计哲学可以理解成一个停车场。停车场有固定的车位数量 N车到了先看车位有空位就进去没空位就在门口排队有车离开便腾出一个空位排在后面的车就能进来。这就是Semaphore的核心逻辑维护一个计数器它不是计数“已经进入的线程数”而是“剩余的许可数”。初始化的时候你通过构造方法指定最大许可数线程要执行临界区代码之前先acquire()领取一个许可执行完之后必须release()归还许可。这样就能保证同一时刻最多只有 N 个线程在临界区里跑。这个“N 许可”模型的好处在于它关注的不是“谁拿到了锁”而是“还有多少个空位”。业务层不需要关心线程调度细节只需要设定好阀门的最大容量至于排队顺序、唤醒方式这些都是 Semaphore 内部通过 AQSAbstractQueuedSynchronizer机制完成的。如果你把数据库连接池想像成车位把并发请求想像成来停车的司机Semaphore 就是那个尽职尽责的保安永远只放 N 辆车进去永远不会多做一步。1.3 方案选型时的核心考量公平与非公平使用 Semaphore 之前你一定会遇到一个问题要不要开启fair模式这直接决定了排队线程的唤醒策略并且会影响吞吐量。非公平模式下后到的线程只要发现还有剩余许可就可以直接“插队”进入临界区根本不需要排队。优势是吞吐量高坏处是极端情况下队列里长时间等待的线程可能饿死始终没机会执行。公平模式会把所有 acquire 请求放进队列先来后到保证每个线程最终都会被执行但吞吐量会有所下降因为唤醒和上下文切换更频繁。我的建议是如果你拿它做控制资源的访问上限比如连接池开公平模式因为资源应该按先来后到分配这才是合理业务但如果你只是做流量的粗略控制允许某些请求稍微“插队”也不会带来副作用那就用非公平模式获取更高的吞吐。2. 核心细节解析与实操要点2.1 Semaphore 核心方法讲透acquire 与 release这组方法是你最常接触的但很多人只知道个大概。acquire()获取一个许可如果当前没有可用许可当前线程会阻塞直到有线程释放许可。它响应中断也就是说在阻塞期间如果收到中断信号会抛出InterruptedException。acquire(int permits)和上面的区别是一次性获取多个许可。比如你需要同时占用 3 个连接才能完成业务就可以一次 acquire 3 个。但这里有个特别容易犯错的地方如果你一次获取多个许可那么 release 时也必须释放同样数量的许可否则信号量的计数会出现漂移久而久之就废了。那release()呢它释放一个许可并且把信号量内部的许可数加 1。释放操作不要求必须由acquire()的线程来调用你可以让另一个线程释放许可但强烈不建议这么干会让代码可读性很差。释放许可的时候如果内部计数器已经在最大值了它不会让数值继续增加最大就是你初始化的那个数。我踩过一个坑有一次用信号量控制 Redis 连接获取的时候用acquire()释放的时候却用了acquire(2)对应的release(2)结果许可数出现过负数新请求永远拿不到许可整个接口直接卡死。从那以后我在任何项目中都会在 release 方法周围加注释标注“此处必需与 acquire 成对使用”。2.2 tryAcquire限流器里最重要的方法tryAcquire()可以说是 Semaphore 所有方法里最适合拿来写限流逻辑的因为它不会无限期阻塞尝试获取许可能拿到就返回 true拿不到就立即返回 false线程不用排队等。这非常符合限流的业务模型并发超了我不让你等我直接告诉你“当前系统繁忙请稍后再试”。如果全部用阻塞式 acquire当流量是阀值的 10 倍时后面的几千个请求全部阻塞在 Semaphore 上线程资源被占满新请求甚至进不来这种“阻塞式等待”就变成了另类的雪崩点。tryAcquire(long timeout, TimeUnit unit)更进一步它允许你在一定时间内等待一个许可超时再放弃。这个方法的真实使用场景是某服务响应时间波动明显如果 500ms 内没拿到许可就走降级逻辑比如返回兜底数据、走缓存。我在限流场景里的推荐配置是核心接口用tryAcquire()做“快速失败”业务需要容忍短暂等待的用tryAcquire(100, TimeUnit.MILLISECONDS)做“超时降级”尽量避免无限等待的acquire()。2.3 availablePermits 与 drainPermits监控和熔断的好帮手除了 acquire 和 releaseSemaphore 还有两个你可能没太注意但很实用的方法availablePermits()可以随时查看当前剩余许可数drainPermits()则会把当前所有可用的许可一次性拿走。这两个方法能派上什么用场我常用availablePermits()做实时监控把信号量的剩余量定期输出到日志或监控大盘比如一旦发现剩余许可长期处于 0 或者稳定低于 10%说明流量已经接近阀值就该触发告警提前扩容或者做限流策略调整。drainPermits()适合做“优雅停机”或“熔断”的操作比如你收到运维通知要下线一个节点此时可以调用 drainPermits() 把所有许可拿走这样新请求直接兜底失败或者拒绝而已经获取了许可的线程会正常执行完实现流量逐渐归零的效果。这两个方法不常被提及但用好了整个信号量的可观测性和运维友好性会提升一大截。2.4 源码思维导图Semaphore 的底层其实是 AQS可能有人好奇Semaphore 内部到底怎么维护这些许可数的。其实它就是基于 AQSAbstractQueuedSynchronizer实现的内部有一个 Sync 类继承 AQS通过 state 变量来记录许可证数量。acquire()实际调用的是 AQS 的acquireSharedInterruptibly核心动作是 CAS 操作尝试把 state 减 1如果 state 小于 0 则表示许可不足当前线程会被加入 AQS 的等待队列并挂起。而release()对应releaseShared使用 CAS 把 state 加 1然后唤醒等待队列中的线程。这个设计是非常经典的无锁并发模型在没有竞争的情况下acquire 和 release 都只是对 state 做一次 CAS性能极高在有竞争的情况下通过 AQS 队列来管理阻塞与唤醒。所以 Semaphore 在实际高并发场景下表现优秀并且不会像 synchronized 一样频繁升级成重量级锁带来性能抖动。3. 实操过程与核心环节实现3.1 场景一秒杀接口的全局限流实战先来一个最常见的场景秒杀接口被脚本疯狂刷量导致后端数据库压力巨大。直接用 Semaphore 做全局限流把并发访问量控制在合理范围。public class SeckillService { private static final Semaphore SEMAPHORE new Semaphore(100, true); public Result doSeckill(Long userId, Long productId) { // 尝试获取许可拿不到直接快速失败避免线程堆积 if (!SEMAPHORE.tryAcquire()) { return Result.busy(当前参与人数过多请稍后重试); } try { // 核心业务逻辑校验库存、秒杀下单 return doSeckillInternal(userId, productId); } finally { // 释放许可必须放在 finally 里异常也要释放 SEMAPHORE.release(); } } }这个代码你可以直接抄作业。注意几个细节设置初始许可数为 100代表同时最多 100 个请求进入核心逻辑开公平模式让先来的请求优先处理tryAcquire 快速失败release 放 finally保证无论正常还是异常许可都能归还。还有一个隐藏细节很多人奇怪为什么不用线程池限流。线程池的maximumPoolSize确实能限制线程数但它面对的是“并发执行的任务数”。如果你接口里大量时间都耗在 IO 等待上比如调用外部服务线程池线程空闲但被占用你再想通过线程池限流其实限不住。Semaphore 是纯计数机制不关心线程状态反而可以精确限制“正在执行业务的请求数”。3.2 场景二数据库连接池的手动实现如果说上面那个场景是限流那这里就是纯资源控制。Java 项目里你用 HikariCP 或者 Druid它们的连接池内部其实就用了 Semaphore 类似的原理。这里我自己动手实现了个轻量版帮助理解。public class SimpleConnectionPool { private final Semaphore semaphore; private final LinkedListConnection pool new LinkedList(); public SimpleConnectionPool(int poolSize) { semaphore new Semaphore(poolSize, true); for (int i 0; i poolSize; i) { pool.addLast(createConnection()); } } public Connection getConnection() { // 先获取许可保证不会超过 poolSize 个线程同时取连接 try { semaphore.acquire(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(获取连接被中断, e); } synchronized (pool) { return pool.removeFirst(); } } public void returnConnection(Connection conn) { synchronized (pool) { pool.addLast(conn); } // 归还许可唤醒等待线程 semaphore.release(); } }这里有几个容易忽略的点获取时先 acquire 再取资源顺序不能反。如果先取资源再 acquire可能在多线程下取出资源数超过连接池容量。归还时先归还资源再 release顺序正相反是先放回资源再释放许可这样才能保证“有资源可拿”的情况下才唤醒等待线程。连接池与信号量组合使用资源控制就变得非常优雅。但要注意这个手写版本没有处理连接失效问题真实项目建议直接用成熟连接池组件不要用自己的轮子替代这里只是帮你理解底层原理。3.3 场景三限流之外信号量做服务自我保护有时候我们不仅要对接口限流还要对某个下游依赖做熔断保护。比如你的服务调用了第三方短信接口对方限制每秒最多 200 次。你用 Semaphore 把每次调用放进一个“许可门”超过就快速失败别让第三方接口被打爆。public class SmsClient { private static final Semaphore SMS_LIMIT new Semaphore(200, true); public SmsResult send(String phone, String content) { boolean acquired false; try { acquired SMS_LIMIT.tryAcquire(50, TimeUnit.MILLISECONDS); if (!acquired) { // 拿不到许可说明并发较高快速降级走异步 MQ 重试 return SmsResult.degraded(); } return doSend(phone, content); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return SmsResult.fail(发送被中断); } finally { if (acquired) { SMS_LIMIT.release(); } } } }这个场景里有个非常关键的细节tryAcquire 返回 true 之后必须记住acquired true状态finally 里只有拿到许可才 release。我见过一些同事在 finally 里无条件调用 release结果明明没有 acquire 成功还是把许可给增回去了导致信号量的许可越来越多限流形同虚设。这种问题排查起来特别隐蔽得留意。同样等待时间的设置最好预估一下下游的响应时间。如果短信接口平均耗时 20ms那 50ms 的等待时间就意味着 2 到 3 个请求并发等待降级率不会太高如果你设置 500ms 等待那在峰值时线程就会大规模阻塞反而会占用你应用的线程资源。3.4 场景四多许可控制的经典案例再补充一个需要一次获取多个许可的场景。比如你要做一个批处理任务每次从任务队列里取 3 个文件同时并行处理限制总处理文件数不超过 100。这意味着信号量的“每个单位”不再是单个请求而是“一个文件”。public class FileBatchProcessor { private static final Semaphore FILE_LIMIT new Semaphore(100, true); private static final int BATCH_SIZE 3; public void processBatch(ListFile files) { try { FILE_LIMIT.acquire(BATCH_SIZE); files.forEach(this::processFile); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { FILE_LIMIT.release(BATCH_SIZE); } } }需要注意acquire(BATCH_SIZE) 这行代码是“拿 3 个许可”如果当前信号量只有 2 个可用许可这个线程会阻塞等待直到有足够的许可一次性被满足。这就有个潜在问题如果许可数长期不足 3比如大多数时候只有 2 个剩余许可批处理线程可能一直等不到足够的许可而饿死。解决思路有两个第一如果必须原子地获取多个许可可以动态调整 BATCH_SIZE 大小让每次获取的数量与剩余许可匹配第二把批处理改成单个文件逐个处理不强制一次性拿 3 个许可。在真实业务里如果批处理任务特别重要我一般更倾向于“可拆分”的设计而不是强制原子化获取多许可。3.5 参数选择的计算思路Semaphore 的初始许可数怎么定这其实没有一个万能公式但有一套非常实用的推算路径。首先你需要明确系统最重要的资源是什么。如果瓶颈在数据库参考数据库最大连接数再留出冗余。比如数据库 max_connections200你的应用有 4 个实例那么每个实例的信号量上限 (200 - 数据库管理的预留连接数) / 4结果大概在 35~40。如果信号量设成 504 个实例同时打满就能占 200 个连接一旦数据库自己有后台任务需要额外连接就会爆。其次要考虑你的接口平均耗时和单机线程数量。举个例子你的接口平均处理时间 100ms单机有 200 个 Tomcat 线程如果信号量上限设 200那么每个线程都能进入临界区等于没有限流。但如果接口平均耗时 500ms信号量设 50那么理论上最多同时处理 50 个请求队列积压的请求会拒绝掉线程处于空闲状态响应能力没问题。最后压测永远是验证参数的最可靠手段。我会先设置一个保守值比如单机 50然后加大并发压测观察 CPU、GC、RT 和错误率。如果所有指标都很健康再把信号量提高到 80 继续压直到找到临界拐点。生产上一般留 70%~80% 的余量不要卡在极限值上。4. 常见问题与排查技巧实录4.1 信号量泄漏release 没被调用的严重后果这是最常见也是最严重的问题。如果业务代码抛异常时 release 没有被执行许可数就会永久减少。假设初始许可 100 个每异常一次少一个到第 100 次异常后信号量变成 0所有请求都会阻塞或快速失败服务相当于瘫痪。排查这种问题的思路先看监控如果 availablePermits 持续下降基本可以断定有线程没 release然后检查所有 acquire 相关代码的 finally 块确保 release 在 finally 中执行。此外可以在 release 处打 WARN 日志做 trace用日志关联请求 ID快速定位是哪个逻辑抛异常导致未释放。还有个注意点tryAcquire返回 false 时不持有许可此时不需要 release返回 true 时必须保证最终 release 执行。我在前面的 SmsClient 示例里用了一个 boolean 标记就是为了处理这个区分。4.2 死锁多信号量嵌套获取很容易出事如果你在一个临界区内又去获取另一个信号量就可能会死锁。比如线程 A 拿下了信号量 S1正在等信号量 S2线程 B 拿下了 S2正在等 S1。两个线程互相等待谁也无法继续。解决思路尽量减少嵌套获取如果无法避免保证所有线程都以相同的顺序获取多个信号量比如先 S1 后 S2这样不会出现死锁。另外也可以通过 tryAcquire 加上超时时间让拿不到第二个信号量的线程主动放弃已有资源直接打破循环等待。4.3 信号量拿到多个许可释放时数量不匹配前面提到过acquire(3) 和 release(3) 必须成对。如果你 acquire(3) 后忘记实际拿了几个许可finally 里写死 release()只归还了一个那计数器就会发生漂移。针对这个问题可以在代码里用一个局部变量记录许可数量finally 里释放时引用这个变量。这里还有一个更隐蔽的坑如果你的业务代码中途 catch 了异常并继续往下走但许可已经在某次操作中被提前 release 了后面 finally 又 release 一次就会造成重复释放。重复释放本身不会报错但会让许可数超过初始值限流效果被稀释。Practically你需要保证 acquire 与 release 一一对应不要在多分支里重复 release。4.4 与 CountDownLatch、CyclicBarrier 搞混很多初学者会把 Semaphore、CountDownLatch、CyclicBarrier 三个并发工具搞混。这里直接用一段话区分Semaphore 是“控制同时访问的线程数”CountDownLatch 是“让多个线程等待某个计数归零”CyclicBarrier 是“让多个线程互相等到达同一个屏障点再一起放行”。三者使用场景完全不同千万别用混。比如你有个需求主线程需要等待所有子线程完成任务这是 CountDownLatch你有 5 个任务要并发跑每个任务完成前要等所有任务准备好这是 CyclicBarrier你要限制同一时刻最多 10 个任务在跑这就是 Semaphore。各司其职工具不存在谁替代谁的关系你清晰地认清语义代码就不会歪。5. 信号量的另类玩法优雅限流升级版5.1 Semaphore 结合时间窗口做动态限流纯靠 Semaphore 做静态限流有一个明显缺点阈值是固定的。线上流量往往会随时间波动比如白天流量大晚上流量低。固定阈值可能白天设高了保护不住系统晚上设低了浪费资源。这时候可以用 Semaphore 定期刷新阈值的方案每隔 1 分钟读取外部配置中心的值动态 new 一个信号量替换旧信号量旧的信号量不能直接丢弃要等已持有的许可都释放完再回收。public class DynamicLimiter { private volatile Semaphore semaphore new Semaphore(100); public void updateLimit(int newLimit) { Semaphore oldSemaphore semaphore; Semaphore newSemaphore new Semaphore(newLimit); semaphore newSemaphore; // 旧的 semaphore 等待全部许可释放后自然失效 for (int i 0; i newLimit; i) { newSemaphore.release(); // 新信号量初始化时没有许可这里补充 } } public boolean tryAcquire() { return semaphore.tryAcquire(); } public void release() { semaphore.release(); } }这个动态切换的思路挺实用但实现时要特别注意等待获取许可的线程可能还阻塞在旧信号量上不能简单丢弃旧对象要给一个缓冲期。比较稳妥的做法是把旧信号量交给一个监控线程等到 availablePermits 恢复到初始值再置为 null让 GC 回收。5.2 从单机限流走向分布式限流很多人会问既然项目是微服务架构单机 Semaphore 够用吗答案是不够。原因很简单不同的服务实例内存各自独立A 实例限流 100B 实例限流 100同一个用户完全可能调用到两台机器合计可以达到 200。真实的全局限流必须依赖一个共享的计数器而这就不是 Semaphore 能解决的问题了。分布式限流的主流方案有几种基于 Redis Lua 脚本实现令牌桶或者滑动窗口基于 ZooKeeper 或者 etcd 来做分布式协调还有基于网关层限流组件如 Sentinel、Hystrix的方式。那 Semaphore 在分布式场景里是不是就没有用了不是的。它非常适合作“本地保护层”分布式限流在上游拦截大头流量但到了每个实例依然会有瞬间的流量尖刺此时在每个实例内部用 Semaphore 做“二次闸门”双保险。这也是我在生产环境的推荐组合网关有总控限流实例内部有本地信号量两层叠加比单独用任何一层都稳。5.3 信号量与线程池的组合拳信号量和线程池是互补的关系。线程池负责控制“有多少个任务在跑”信号量可以负责控制“有多少个任务在执行敏感操作”。你可能会问这不冲突吗实际上它们是不同维度的限制。举个例子一个业务服务有 200 个线程可用高峰期有一批任务要调用外部接口外部接口只允许 30 个并发。如果你只靠线程池限制200 个线程可能同时在调用外部接口直接打爆对方。如果你只靠信号量200 个线程虽然大部分会被信号量堵住但它们依然占用着线程资源。两者结合线程池提供基本的并发执行能力信号量保护外部依赖不被冲垮。在真实的微服务治理中这是非常常见的组合拳。6. 避坑指南与独家经验总结6.1 关于初始化许可数的“坑中坑”我在第 3 节讲了参数计算思路这里再补充一个容易被忽略的细节Semaphore 的许可数一旦在构造时设定除非调用 release 多次或者 drain否则不会自己改变。如果你在配置中心调整了阈值下发了新信号量之后旧信号量如果还持有许可应该想办法让它在业务低峰期自然释放否则会出现新旧信号量并存期间实际并发量变成两倍的问题。比较成熟的做法是动态更新时先创建一个新的 Semaphore并设置成新阈值然后利用一个缓冲区策略把旧的 Semaphore 的 acquire 方法全部改成 tryAcquire让旧线程不再长期阻塞然后等它归零再销毁。这个过程看起来简单实际操作时务必通过监控来全程观察不可盲目上线。6.2 代码细节tryAcquire 拿不到许可到底该干什么拿到不到许可的最终处理方式很多人没想清楚直接抛出异常或者返回一个固定的错误码用户端体验很差。其实这里应该结合业务来决策。如果是秒杀场景“挤不进去”本身就符合预期返回“已售罄”或“请稍后重试”即可如果是查询类接口可以降级到本地缓存或历史数据如果是核心支付链路则不建议直接 quick fail而是应该设置一个较长的超时时间比如 500ms让请求有等待的余地。一般来说写限流代码前先想清楚“超出阈值的行为语义是什么”比单纯加一个信号量重要得多。6.3 信号量的可观测性建设最后分享一个我在团队里强力推行的做法所有 Semaphore 实例都要能够被监控看到。具体做法是在创建信号量时统一通过一个工厂方法创建并注册到一个 Metrics 容器中定时采集 availablePermits 和 queueLength 指标。AQS 里本身有 getQueueLength() 方法能查看有多少线程在等待许可。当这个值长期大于 0且 availablePermits 长时间为 0说明系统已经处于饱和状态要么扩容要么排查是否存在信号量泄漏。这套监控体系搭好之后线上限流器的“健康度”一目了然不会再等到用户投诉才发现系统已经瘫痪了半天。我个人在实际操作中还有一个体会信号量最好用有意义的命名包裹一层。比如封装一个 RateLimiterHolder 类里面包含 Semaphore 实例、名字、阈值、指标采集器等字段统一对外提供 tryAcquire 和 release 方法。这样你部署了十个限流点之后还能分清哪个是数据库保护、哪个是短信网关保护、哪个是秒杀接口限制排查问题时才不会一脸懵。这套方法我用了几年每次团队新人上手也能很快定位到问题。

相关新闻

一条工时数据走完 Gauzy 全链路:从打卡到工资单、发票、报表

一条工时数据走完 Gauzy 全链路:从打卡到工资单、发票、报表

一条工时数据走完 Gauzy 全链路:从打卡到工资单、发票、报表 【免费下载链接】ever-gauzy Ever Gauzy™ - Open Business Management Platform (ERP/CRM/HRM/ATS/PM) - https://gauzy.co 项目地址: https://gitcode.com/GitHub_Trending/ev/ever-gauzy Ever …

2026/10/10 19:38:09 阅读更多 →
SpringBoot+SSM办公管理系统开发实践:从权限审批到调试全解析

SpringBoot+SSM办公管理系统开发实践:从权限审批到调试全解析

我几年前接过一个办公管理系统项目,技术栈是现在很多人在用的 Java SpringBoot SSM。当时甲方的要求很简单:把人、事、流程管起来,把请假、报销、用章这些日常审批线上化。结果一聊需求才发现,办公管理系统这东西,看…

2026/10/10 19:38:09 阅读更多 →
AI Agent 出问题时,不要只看最终回答:用 TaoToken 做一次请求级调试

AI Agent 出问题时,不要只看最终回答:用 TaoToken 做一次请求级调试

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

2026/10/10 19:38:09 阅读更多 →

最新新闻

两节点电力系统高斯-赛德尔潮流计算:MATLAB实现与常见坑解析

两节点电力系统高斯-赛德尔潮流计算:MATLAB实现与常见坑解析

潮流计算是电力系统分析里绕不开的一步。今天聊一个很有意思的入门题目:两节点电力系统的高斯-赛德尔(Gauss-Seidel)潮流计算,用MATLAB把PQ节点(母线2)的电压幅值和相角求出来。这个例子虽然网络规模小到只…

2026/10/10 22:57:35 阅读更多 →
Cursor如何成为系统工程师的调试中枢:上下文保真度实战指南

Cursor如何成为系统工程师的调试中枢:上下文保真度实战指南

1. 这不是“用AI写代码”的教程,而是个真实开发者在Cursor里重构工作流的全过程“PStack 作者分享:我怎么用 Cursor”——看到这个标题,你大概率会以为这是一篇轻飘飘的工具体验文,配几张截图、列几条快捷键、再夸一句“真香”。但…

2026/10/10 22:57:35 阅读更多 →
为编程智能体开发三个插件:余额胶囊、任务面板与番茄钟实践

为编程智能体开发三个插件:余额胶囊、任务面板与番茄钟实践

大概三个月前,我把 DeepSeek 的模型能力包装成一个跑在本地的编程智能体,平时帮我看代码、改接口、跑测试、整理 commit 信息。它确实能干活,但用着用着,我发现它太像一台只进不出的黑盒子:看不见它下一步打算做什么&a…

2026/10/10 22:57:35 阅读更多 →
赢 iPhone 17!快手 KwaiKAT 开发挑战赛刚启动:真金白银抢开发者

赢 iPhone 17!快手 KwaiKAT 开发挑战赛刚启动:真金白银抢开发者

赢 iPhone 17!快手 KwaiKAT 开发挑战赛刚启动:真金白银抢开发者 【免费下载链接】KAT-Coder-V2.5-Dev 项目地址: https://ai.gitcode.com/hf_mirrors/Kwaipilot/KAT-Coder-V2.5-Dev 短视频巨头下场做 AI 编程,已经不是新闻&#xff1…

2026/10/10 22:57:35 阅读更多 →
把稍后读变成私人知识库:wallabag/Readeck 增量同步进 Hister

把稍后读变成私人知识库:wallabag/Readeck 增量同步进 Hister

把稍后读变成私人知识库:wallabag/Readeck 增量同步进 Hister 【免费下载链接】hister Your own search engine 项目地址: https://gitcode.com/GitHub_Trending/hi/hister "稍后读"是大多数人的数字囤积症:收藏进 wallabag、Readeck 里…

2026/10/10 22:57:35 阅读更多 →
Cursor实战案例-图形图像-44-海报设计代码化:利用Pillow在图片指定区域动态叠加高精文字与透明阴影底栏|TaoToken 统一 Key 调用图像处理脚本

Cursor实战案例-图形图像-44-海报设计代码化:利用Pillow在图片指定区域动态叠加高精文字与透明阴影底栏|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/10 22:56:34 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/10 11:14: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/10 5:23:50 阅读更多 →
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/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练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/10 10:38:42 阅读更多 →