C# and Rust async models are based on top of state machines and cooperative yielding back to the executor. The difference between C# and Rust is a tradeoff of either the ability to just not think and use async/await naturally with really nice defaults but paying for those with heap allocations of state captured by continuations or dealing with the memory model explicitly which requires more effort but gives you fully configurable and/or deterministic behavior, that can (and usually does) achieve far lower overhead.
Java uses green (virtual) threads where the runtime can preempt (pause/suspend) them (keeping virtual thread stack in memory) and schedule the execution of a different green thread on top of the current physical one, not dissimilar to C#/Rust as they achieve so explicitly, where the next work item is executed by the threadpool once the current one yields.
C# and Rust async models are based on top of state machines and cooperative yielding back to the executor. The difference between C# and Rust is a tradeoff of either the ability to just not think and use async/await naturally with really nice defaults but paying for those with heap allocations of state captured by continuations or dealing with the memory model explicitly which requires more effort but gives you fully configurable and/or deterministic behavior, that can (and usually does) achieve far lower overhead.
Java uses green (virtual) threads where the runtime can preempt (pause/suspend) them (keeping virtual thread stack in memory) and schedule the execution of a different green thread on top of the current physical one, not dissimilar to C#/Rust as they achieve so explicitly, where the next work item is executed by the threadpool once the current one yields.