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

AQS (AbstractQueuedSynchronizer) Core

AQS (AbstractQueuedSynchronizer) CoreAQS is the foundation of almost everything injava.util.concurrent. It provides: anint state a CLH wait queue. Subclasses just define whatstatemeans.What’s Built on AQSAQS ├── ReentrantLock (state hold count) ├── ReentrantReadWriteLock (state read count | write count) ├── Semaphore (state available permits) ├── CountDownLatch (state remaining count) ├── FutureTask (state task status) └── ThreadPoolExecutor (worker state via AQS)When to Use EachSubclassAQS Modestate MeaningWhen to UseReentrantLockExclusiveHold count (reentrant depth)Need tryLock, timeout, fairness, or multiple conditions. Upgrade fromsynchronizedwhen its features aren’t enough. Seereentrant-lock.mdReentrantReadWriteLockShared (read) Exclusive (write)Upper 16 bits readers, lower 16 writer countRead-heavy workloads where multiple readers can proceed concurrently but writes must be exclusive. Seeread-write-lock.mdSemaphoreSharedAvailable permitsRate limiting, connection pools, bounding concurrent access to N threads. No ownership — any thread can release. Seesemaphore.mdCountDownLatchSharedRemaining countOne-shot “wait for N events” — workers count down, coordinator awaits zero. Cannot reset. Seecount-down-latch.mdFutureTaskCustomTask status (NEW → COMPLETING → NORMAL/EXCEPTIONAL)Wraps a Callable into a cancellable, awaitable task.get()parks via AQS until the task completes. Used internally by ExecutorService. See../java-thread-internals.mdThreadPoolExecutorExclusive (per worker)Worker run stateEach worker thread is an AQS-based exclusive lock.shutdown()interrupts idle workers by tryingtryLock()on each — if it succeeds, the worker is idle. Running workers hold their own lock sotryLock()fails → not interrupted. See../java-thread-internals.mdNote: CyclicBarrier is NOT built on AQS — it uses ReentrantLock Condition internally. Seecyclic-barrier.md.StampedLock is NOT built on AQS — it uses its own 64-bit state. Seestamped-lock.md.Inheritance ChainAbstractOwnableSynchronizer ← exclusiveOwnerThread (who holds the lock) │ AbstractQueuedSynchronizer ← state, head, tail, CLH queue, acquire/release logic │ ├── ReentrantLock.Sync ← tryAcquire, tryRelease (see reentrant-lock.md) ├── Semaphore.Sync ← tryAcquireShared, tryReleaseShared (see semaphore.md) ├── CountDownLatch.Sync ← tryAcquireShared, tryReleaseShared (see count-down-latch.md) └── ReentrantReadWriteLock ← packed state (see read-write-lock.md)exclusiveOwnerThreadis in the parent class because not all AQS subclasses need it —SemaphoreandCountDownLatchuse shared mode (no single owner).Core Structure// AbstractOwnableSynchronizerprivatetransientThreadexclusiveOwnerThread;// who holds the lock (exclusive mode only)// AbstractQueuedSynchronizervolatileintstate;// meaning depends on subclassvolatileNodehead;// head of CLH wait queue (dummy node) — null until first contentionvolatileNodetail;// tail of CLH wait queue — null until first contention// Subclasses override — define what acquire and release mean:protectedbooleantryAcquire(intarg){throwUOE;}protectedbooleantryRelease(intarg){throwUOE;}protectedinttryAcquireShared(intarg){throwUOE;}protectedbooleantryReleaseShared(intarg){throwUOE;}No Contention — Fast Path (No Queue)When no contention, the queue is never created. OnlystateandexclusiveOwnerThreadare used:// tryAcquire — no contentionif(state0CAS(state,0,1)){exclusiveOwnerThreadcurrentThread;// record ownerreturntrue;// done — no queue touched}No contention: ┌──────────────────────────────────┐ │ state 1 │ ← lock is held │ exclusiveOwnerThread Thread-0 │ ← held by whom │ head null │ ← no queue (never needed) │ tail null │ └──────────────────────────────────┘ After release: ┌──────────────────────────────────┐ │ state 0 │ ← lock is free │ exclusiveOwnerThread null │ ← nobody owns it │ head null │ ← still no queue │ tail null │ └──────────────────────────────────┘The queue is lazily initialized only on first contention (whentryAcquirefails).CLH Queue (Craig, Landin, Hagersten)A doubly-linked FIFO queue.headandtailare the two endpoints. Each node holds a thread and a wait status:staticclassNode{volatileintwaitStatus;// SIGNAL(-1), CANCELLED(1), CONDITION(-2), PROPAGATE(-3), 0volatileNodeprev;volatileNodenext;volatileThreadthread;}head (dummy) → [Node: T-1, SIGNAL] → [Node: T-2, SIGNAL] → [Node: T-3, 0] ← tail (parked) (parked) (just enqueued)Original CLH is a singly-linked spin lock. AQS replaces spinning withpark/unpark(no CPU waste) and addsprevpointers for node cancellation.Two key properties:No contention between enqueue and dequeue (different ends):Dequeue (head side): Enqueue (tail side): head head.next CAS(tail, old, new) touches: head pointer touches: tail pointer → different memory locations → NO conflictEach thread only interacts with predecessor (no thundering herd):CLH — wake only successor: unlock() → unpark T-1 only (head.next) T-1 wakes → acquires → becomes head → unlocks → unpark T-2 → one-to-one handoff, 0 wasted wakeupsAQS maintains a dummy head so head and tail are separate nodes even with one entry. Queue is lazily initialized on first contention:privateNodeenq(Nodenode){for(;;){Nodettail;if(tnull){if(CAS(head,null,newNode()))// create dummy headtailhead;}else{node.prevt;if(CAS(tail,t,node)){// append to tailt.nextnode;returnt;}}}}Exclusive Acquire Flow (Full Code)// Entry point: ReentrantLock.lock() → AQS.acquire(1)publicfinalvoidacquire(intarg){if(!tryAcquire(arg)// fast path: try CASacquireQueued(addWaiter(Node.EXCLUSIVE),arg))// slow path: enqueue parkselfInterrupt();// restore interrupt flag if needed}staticvoidselfInterrupt(){Thread.currentThread().interrupt();// re-set the interrupt flag}addWaiter — Fast Path Slow PathprivateNodeaddWaiter(Nodemode){NodenodenewNode(Thread.currentThread(),mode);Nodepredtail;if(pred!null){// FAST PATH: queue existsnode.prevpred;// link prev BEFORE CASif(compareAndSetTail(pred,node)){// one CAS attemptpred.nextnode;// link next AFTER CASreturnnode;// done!}}enq(node);// SLOW PATH: queue empty or CAS failedreturnnode;}Fast path: queue exists, one CAS succeeds → done (no loop). Slow path (enq): queue empty (create dummy head) or CAS race (retry in loop).Note:enqdoes NOT set predecessor’swaitStatus. SIGNAL is set later byshouldParkAfterFailedAcquire— only when the thread is actually about to park.acquireQueued — Spin/Park LoopfinalbooleanacquireQueued(finalNodenode,intarg){booleaninterruptedfalse;for(;;){finalNodepnode.predecessor();if(pheadtryAcquire(arg)){// am I next? can I get it?setHead(node);// become new dummy headp.nextnull;// help GC old dummyreturninterrupted;// tell caller: was I interrupted?}if(shouldParkAfterFailedAcquire(p,node))// set SIGNAL on predecessorinterrupted|parkAndCheckInterrupt();// park here, check interrupt on wake}}privatefinalbooleanparkAndCheckInterrupt(){LockSupport.park(this);// park (woken by unpark OR interrupt)returnThread.interrupted();// was I woken by interrupt? (clears flag)}voidsetHead(Nodenode){headnode;node.threadnull;// head is always a dummynode.prevnull;}interruptedtracks whether the thread was interrupted while parked.lock()doesn’t throw — it swallows the interrupt but remembers it. WhenacquireQueuedreturnstrue,acquire()callsselfInterrupt()to restore the flag so the caller can check it later.lock(): interrupt → remember → keep waiting → got lock → restore flag lockInterruptibly(): interrupt → cancel → throw InterruptedException → no lockOnly the thread right after head attemptstryAcquire(). Others stay parked.Dequeue: the acquiring thread becomes the new dummy head. Old dummy is disconnected for GC. Nodes are removed one at a time — no bulk clear:Before T-1 acquires: head (dummy) → [T-1] → [T-2] ← tail After T-1 acquires: head (T-1, dummy) → [T-2] ← tail (old dummy → GC) After T-2 acquires: head (T-2, dummy) ← tail (queue empty)shouldParkAfterFailedAcquire — Set SIGNAL, Decide to ParkprivatestaticbooleanshouldParkAfterFailedAcquire(Nodepred,Nodenode){intwspred.waitStatus;if(wsNode.SIGNAL)// Case 1: already SIGNALreturntrue;// → safe to parkif(ws0){// Case 2: CANCELLEDdo{node.prevpredpred.prev;}// skip cancelled nodeswhile(pred.waitStatus0);pred.nextnode;returnfalse;// → try acquire again}else{// Case 3: 0 or PROPAGATECAS(pred.waitStatus,ws,Node.SIGNAL);// set SIGNALreturnfalse;// → try acquire one more time}}Always takes at least 2 loop iterations before parking — first to set SIGNAL, second to confirm and park. This gives one extratryAcquirechance (lock might have been released).LockSupport Permit — Why unpark Before park Is SafeLockSupportuses a per-thread permit (0 or 1).unpark()beforepark()is NOT lost:unpark(thread): permit 1 park(): permit 1? → consume, return immediately (no sleep) permit 0? → sleep until unparkIf release happens betweenshouldParkreturning true andpark(), theunparksets the permit. Whenpark()is called, it sees the permit and returns immediately. No thread is ever lost.Exclusive Release Flowpublicfinalbooleanrelease(intarg){if(tryRelease(arg)){// state-- → state 0?Nodehhead;if(h!nullh.waitStatus!0)unparkSuccessor(h);// wake head.nextreturntrue;}returnfalse;// still held (reentrant)}tryReleaseSubclass-specific. Seereentrant-lock.mdfor ReentrantLock’s reentrant decrement logic.Theh.waitStatus ! 0check inrelease(): if 0, no successor has set SIGNAL yet — either queue is empty or successor hasn’t parked yet (it will trytryAcquireand succeed since state is now 0).unparkSuccessor — Find and Wake the Next ThreadvoidunparkSuccessor(Nodenode){CAS(node.waitStatus,SIGNAL,0);// reset for future useNodesnode.next;// fast path: head.nextif(snull||s.waitStatus0){// null or CANCELLED?snull;for(Nodettail;t!nullt!node;tt.prev)// walk backwardsif(t.waitStatus0)st;// find valid successor}if(s!null)LockSupport.unpark(s.thread);}Why walk backwards?previs always reliable (set before CAS).nextcan be null (enqueue window), self-link (cancelled), or stale.Shared Acquire/Release (Semaphore, CountDownLatch)Multiple threads can acquire simultaneously. Release propagates — waking cascades:Semaphore(3) — 3 threads waiting, 3 permits released: releaseShared → wake T-1 T-1 acquires → still permits → wake T-2 (PROPAGATE) T-2 acquires → still permits → wake T-3 (PROPAGATE) T-3 acquires → no more → stopSeesemaphore.mdfor full shared acquire internals and PROPAGATE cascade details.tryAcquire() — Subclass-SpecificEach subclass defines what “acquire” means by overridingtryAcquire(exclusive) ortryAcquireShared(shared). AQS provides the queuing and parking — subclasses only define the CAS logic onstate. Seereentrant-lock.mdfor fair/unfair tryAcquire details.TheacquiresParameterForReentrantLock, always1. Exists because AQS is a general framework:ReentrantLock: state reentrant count tryAcquire(1) → state 1 Semaphore(3): state available permits tryAcquire(1) → state - 1 tryAcquire(2) → state - 2 CountDownLatch: state remaining count tryRelease(1) → state - 1 ReadWriteLock: state packed 32 bits upper 16 read count tryAcquireShared(1) → upper 1 lower 16 write count tryAcquire(1) → lower 1How Different Synchronizers Use AQSReentrantLock: tryAcquire(1): state 0? CAS(0,1), owner me. owner me? state tryRelease(1): state-- → state 0? owner null → wake successor Semaphore(3): tryAcquireShared(1): avail state; CAS(state, avail, avail-1) tryReleaseShared(1): CAS(state, c, c1) → propagate CountDownLatch(5): tryAcquireShared(1): state 0 ? 1 : -1 (only acquire when count is 0) tryReleaseShared(1): CAS(state, c, c-1) → c-1 0? wake all ReadWriteLock: state upper 16 bits shared read count state lower 16 bits exclusive write count tryAcquire(1): write lock (lower bits) tryAcquireShared(1): read lock (upper bits)Sync Queue vs Condition QueueAQS supports two types of queues. The sync queue is built-in (one per AQS instance). Condition queues are created on demand vianewCondition()and are only available for exclusive-mode subclasses (e.g., ReentrantLock).Sync Queue (lock waiters): I want the lock but someone else has it head → [Thread-1] → [Thread-2] → tail Condition Queue (condition waiters): I have the lock but a business condition isnt met first → [Thread-3] → [Thread-4] → lastsignal()moves a thread from condition queue to sync queue (still needs to re-acquire lock).await()fully releases the lock, parks in the condition queue, then re-acquires viaacquireQueuedwhen signaled.Seereentrant-lock.mdfor condition queue usage patterns (producer-consumer) andawait()/signal()internals.Lock Acquisition StrategiesAQS supports four waiting policies. The queue mechanics are the same — only the parking/cancellation behavior differs. Seereentrant-lock.mdfor usage details of each strategy (lock(),lockInterruptibly(),tryLock(),tryLock(timeout)).waitStatus Deep DivewaitStatustells the system what to do with a node and its successor:0 → Initial state (just enqueued) SIGNAL (-1) → When I release, I MUST unpark my successor CANCELLED (1) → I gave up waiting (timeout/interrupt), skip me CONDITION (-2) → Im in a condition queue, not the sync queue PROPAGATE (-3) → Shared release should propagate to next nodevolatilebecause it’s read by one thread (predecessor) and written by another (successor setting SIGNAL, or node itself setting CANCELLED).SIGNAL is set by the successor, not the node itself — Thread-2 arrives, sets predecessor.waitStatus SIGNAL, then parks safely knowing predecessor will wake it.cancelAcquire() — Full Cancel FlowCalled when a thread gives up waiting — fromlockInterruptibly()(interrupt),tryLock(timeout)(timeout), or any exception:park() → give up CPU, stay in queue unpark → get CPU back, still in queue tryAcquire success → get CPU get lock → become head (normal path) tryAcquire fail timeout/interrupt → get CPU, no lock → cancelAcquire (cleanup)privatevoidcancelAcquire(Nodenode){if(nodenull)return;// Step 1: Clear thread reference (this node is dead)node.threadnull;// Step 2: Skip cancelled predecessors (walk backwards)Nodeprednode.prev;while(pred.waitStatus0)node.prevpredpred.prev;NodepredNextpred.next;// Step 3: Mark as CANCELLEDnode.waitStatusNode.CANCELLED;// Step 4: Unlink — three casesif(nodetailcompareAndSetTail(node,pred)){// Case 1: tail → remove from endcompareAndSetNext(pred,predNext,null);}elseif(pred!head(pred.waitStatusNode.SIGNAL||compareAndSetWaitStatus(pred,pred.waitStatus,Node.SIGNAL))pred.thread!null){// Case 2: middle → link pred to my successor (skip me)Nodenextnode.next;if(next!nullnext.waitStatus0)compareAndSetNext(pred,predNext,next);}else{// Case 3: right after head → wake my successorunparkSuccessor(node);}// Step 5: Self-link for GCnode.nextnode;}The three cases:Case 1: node is tail Before: head → [pred] → [node] ← tail After: head → [pred] ← tail Case 2: node is in the middle Before: head → [pred SIGNAL] → [node] → [T-3] After: head → [pred SIGNAL] ────────→ [T-3] (skip node) Case 3: node is right after head Before: head → [node] → [T-3 parked] After: unparkSuccessor(node) → wake T-3 T-3 wakes → shouldPark skips cancelled nodes → acquires or re-parksCase 3 must callunparkSuccessor— if it just unlinked without waking, the successor would sleep forever (nobody to unpark it).The Big PictureAQS state (int) CLH sync queue park/unpark Exclusive (ReentrantLock): acquire: tryAcquire → fail → enqueue → park → wake → tryAcquire → success release: tryRelease → state 0 → unpark successor Shared (Semaphore, CountDownLatch): acquire: tryAcquireShared → fail → enqueue → park → wake → propagate release: tryReleaseShared → unpark propagate Condition: await: condition queue → fully release → park signal: condition queue → sync queue → normal acquire cycleWhat Locks Actually ManageLocks manage access to shared heap memory — not the stack. Each thread has its own private stack (local variables, method frames). The shared heap is where contention happens:Thread-0 stack (private): Thread-1 stack (private): ┌──────────────────┐ ┌──────────────────┐ │ local var x 10 │ │ local var y 20 │ └──────────────────┘ └──────────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────────────┐ │ Shared Heap Memory │ │ counter 42 ← BOTH threads read/write this │ └─────────────────────────────────────────────────┘ Without lock: both do counter → race condition With lock: one at a time → safeAQS itself lives on the heap —state,head,tail, and allNodeobjects are heap-allocated. Unlikesynchronized’s lightweight lock (which uses a stack-based lock record as an optimization), AQS has no stack-resident structures.

相关文章:

AQS (AbstractQueuedSynchronizer) Core

AQS (AbstractQueuedSynchronizer) Core AQS is the foundation of almost everything in java.util.concurrent. It provides: an int state a CLH wait queue. Subclasses just define what state means. What’s Built on AQS AQS ├── ReentrantLock (state h…...

AI编程时代,人类程序员还剩下什么?堂

故障表现 发现请求集群 demo 入口时卡住,并且对应 Pod 没有新的日志输出 rootce-demo-1:~# kubectl get pods -n deepflow-otel-spring-demo -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NO…...

Harness 中的事件溯源:以事件日志重建状态

Harness 中的事件溯源:以事件日志重建全链路 DevOps 状态 引言 痛点引入 作为全链路 DevOps 平台,Harness 每天会处理 数百万到数千万级别的用户/系统操作:开发者点击“启动流水线”、Feature Flag 规则引擎执行批量开关切换、云成本扫描器…...

智能车竞赛独轮组信标灯系统全解析:从硬件选型到实战调试技巧

智能车竞赛独轮组信标灯系统全解析:从硬件选型到实战调试技巧 信标灯系统作为智能车竞赛独轮组的核心模块,直接决定了车模的导航精度和比赛成绩。一套稳定高效的信标灯系统需要硬件选型、信号处理、算法优化和实战调试的完美配合。本文将深入剖析信标灯系…...

彻底告别OpenClaw使用焦虑:我给他装上了“透视眼”和“批量克隆模组贾

指令替换 项目需求:将加法指令替换为减法 项目目录如下 /MyProject ├── CMakeLists.txt # CMake 配置文件 ├── build/ #构建目录 │ └── test.c #测试编译代码 └── mypass2.cpp # pass 项目代码 一,测试代码示例 test.c // test.c #includ…...

从ReLU到GELU:非线性投影如何提升注意力机制的效果?

从ReLU到GELU:非线性投影如何重塑注意力机制的边界? 在Transformer架构席卷NLP领域的今天,注意力机制已成为深度学习模型的标准组件。但鲜少有人讨论的是,那些隐藏在QKV投影层后的非线性激活函数,才是真正决定注意力&q…...

04-Java JDK, JRE和JVM

Java JDK, JRE和JVM 在本教程中,您将了解JDK,JRE和JVM。您还将学习它们之间的主要区别。 什么是JVM? JVM(Java虚拟机)是使您的计算机运行Java程序的抽象机。 运行Java程序时,Java编译器首先将Java代码编…...

从零实现富文本编辑器#-React可编辑节点的组件预设渤

1. 智能软件工程的范式转移:从库集成到原生框架演进 在生成式人工智能(Generative AI)从单纯的文本生成向具备自主规划与执行能力的“代理化(Agentic)”系统跨越的过程中,.NET 生态系统正在经历一场自该平…...

Windows 11系统优化终极指南:Win11Debloat一键清理与隐私保护工具

Windows 11系统优化终极指南:Win11Debloat一键清理与隐私保护工具 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declu…...

如何在6小时内将小说变成爆款推文视频?TaleStreamAI完整指南

如何在6小时内将小说变成爆款推文视频?TaleStreamAI完整指南 【免费下载链接】TaleStreamAI AI小说推文全自动工作流,自动从ID到视频 项目地址: https://gitcode.com/gh_mirrors/ta/TaleStreamAI 你是否曾想过,将一本精彩的小说在短短…...

和AI一起搞事情#:边剥龙虾边做个中医技能来起号图

1. 核心概念 在 Antigravity 中,技能系统分为两层: Skills (全局库):实际的代码、脚本和指南,存储在系统级目录(如 ~/.gemini/antigravity/skills)。它们是“能力”的本体。 Workflows (项目级)&#xff1a…...

Mysql的行级锁到底是怎么加的?醒

1. 架构背景与演进动力 1.1 从单体到碎片化:.NET 的开源征程 在.NET Framework 时代,构建系统主要围绕 Windows 操作系统紧密集成,采用传统的封闭式开发模式。然而,随着.NET Core 的推出,微软开启了彻底的开源与跨平台…...

一个简洁易用的 Delphi JSON 封装库,基于 System.JSON`单元封装,提供更直观的 API文

一、前言:什么是 OFA VQA 模型? OFA(One For All)是字节跳动提出的多模态预训练模型,支持视觉问答、图像描述、图像编辑等多种任务,其中视觉问答(VQA)是最常用的功能之一——输入一张…...

TCLB(CUDA Lattice Boltzmann)项目介绍

文章目录TCLB 项目介绍核心特性1. 高性能计算架构2. 核心功能与耦合能力3. 跨平台支持快速使用流程1. 安装与编译2. 运行仿真依赖要求核心依赖可选依赖快速安装依赖学术引用开发与贡献许可证TCLB应用领域1. 流体动力学基础与工程仿真2. 流固耦合(LBM-DEM&#xff09…...

.NET 诊断技巧 | 日志框架原理、手写日志框架学习汕

一、 什么是 AI Skills:从工具级到框架级的演化 AI Skills(AI 技能) 的概念最早在 Claude Code 等前沿 Agent 实践中被强化。最初,Skills 被视为“工具级”的增强,如简单的文件读写或终端操作,方便用户快速…...

OpenMatrix 架构解析:基于 Harness 思想的 AI 任务编排系统

引言:AI 编码的信任危机 AI 编码工具已经非常强大,但用户仍然不敢完全信任。为什么? 第一层:AI 补全代码(Copilot)→ 解决「写」的问题 第二层:AI 对话编程(Claude Code&#xff0…...

C语言入门:秒懂数据类型

刚接触C语言,我们总会遇到int、char、float这些关键词,很多同学觉得麻烦,甚至想只用一种类型写完全部代码。其实数据类型是编程的基础,理解它,才能写出规范、少出错的程序。简单来说,数据类型就是给变量规定…...

本周补题 4/5 -- 4/12

Pta 天梯赛:9 10 11 14 13牛客138:A B C D E F...

C 语言数据类型全解析:从基础到实战

数据类型是 C 语言的基石,也是每个 C 语言初学者必须掌握的核心概念。它决定了变量在内存中占用的空间大小、存储方式以及取值范围。本文将从最基础的概念讲起,系统梳理 C 语言所有数据类型,包含详细的语法、示例代码和实战技巧,帮…...

2026年杭州AI搜索营销新宠横评:5大系统供应商性能实测+选型建议

各位老板,2026年了,还在为传统SEO流量下滑发愁吗?AI搜索的流量入口已经明牌,但市面上的GEO(生成式引擎优化)工具和服务商五花八门,选谁合作才能不踩坑、不掉队?今天,我们…...

Vivado FFT IP核避坑指南:从Matlab数据生成到FPGA验证的完整流程

Vivado FFT IP核实战避坑指南:从Matlab数据生成到FPGA验证的全链路解析 在FPGA信号处理领域,FFT(快速傅里叶变换)是实现频域分析的核心运算单元。Xilinx Vivado提供的FFT IP核虽然功能强大,但在实际工程落地过程中&…...

动态数码管鬼影问题全攻略:从51单片机消影代码到TM1637芯片方案

动态数码管鬼影现象深度解析与工程实践指南 1. 数码管显示原理与鬼影成因 数码管作为嵌入式系统中最常见的显示器件之一,其工作原理直接影响着显示质量。我们先从基础结构说起: 数码管内部构造: 7段LED排列成"8"字形(部…...

基于STM32F407与W5500的HAL库TCP通信实战指南

1. 硬件准备与连接 搞嵌入式开发的朋友都知道,硬件连接是第一步也是最容易出错的地方。我刚开始用STM32F407和W5500时,就因为SPI接线问题折腾了好几天。这里分享下我的经验,帮你少走弯路。 首先说说W5500这个模块,它是一款全硬件T…...

【IIC通信】Chap.2 从“线与”到“时序”:I2C总线协议深度解析与实战信号分析

1. 从线与逻辑看I2C总线冲突的本质 第一次用示波器抓取I2C波形时,我盯着那条"不听话"的SDA线陷入了沉思——为什么总线上某个设备拉低电平后,其他设备输出的高电平就消失了?这个现象背后正是I2C最精妙的"线与"设计。所有…...

【实战】ESP32 + LN298N 驱动编码器推杆:从零搭建行程闭环控制系统

1. 硬件选型与系统架构设计 这个项目最核心的硬件就是ESP32开发板、LN298N电机驱动模块和带编码器的电动推杆。先说ESP32,我强烈推荐使用ESP32-S3系列,相比S2多了几个硬件PWM通道,对于多电机控制特别友好。实测下来,ESP32的硬件PW…...

告别物理JTAG:手把手在KV260 PYNQ上配置XVC远程调试接口(含Vivado Block Design)

告别物理JTAG:KV260 PYNQ环境下的XVC远程调试实战指南 调试Zynq平台PL逻辑时,传统JTAG连接常受限于物理接触和线缆长度。去年在开发一个工业视觉项目时,产线设备与调试台相距30米,来回插拔JTAG不仅效率低下,还导致多次…...

【青少年CTF S1·2026 公益赛】easy_php

<?php // 屏蔽报错&#xff0c;增加一点黑盒难度 error_reporting(0); // TIPS: FLAG在根目录下class Monitor {private $status;private $reporter;public function __construct() {$this->status "normal";$this->reporter new Logger();}public funct…...

Serilog:从结构化日志认知到 .NET 工程落地何

1. 前言 本文详细介绍如何使用 kylin v10 iso 文件构建出 docker image&#xff0c;docker 版本为 20.10.7。 2. 构建 yum 离线源 2.1. 挂载 ISO 文件 mount Kylin-Server-V10-GFB-Release-030-ARM64.iso /media 2.2. 添加离线 repo 文件 在/etc/yum.repos.d/下创建kylin-local…...

Spring Cloud进阶--分布式权限校验OAuth淄

一、核心问题及解决方案&#xff08;按踩坑频率排序&#xff09; 问题 1&#xff1a;误删他人持有锁——最基础也最易犯的漏洞 成因&#xff1a;释放锁时未做身份校验&#xff0c;直接执行 DEL 命令删除键。典型场景&#xff1a;服务 A 持有锁后&#xff0c;业务逻辑耗时超过锁…...

ANARCI抗体序列编号终极指南:从零基础到实战应用的完整教程

ANARCI抗体序列编号终极指南&#xff1a;从零基础到实战应用的完整教程 【免费下载链接】ANARCI Antibody Numbering and Antigen Receptor ClassIfication 项目地址: https://gitcode.com/gh_mirrors/an/ANARCI ANARCI&#xff08;Antibody Numbering and Antigen Rece…...