This is probably best thought of from the Erlang perspective; where an OS level thread is called a "scheduler" and what as an Erlang programmer you think of as a thread (called an "erlang process") of which you can have quite literally hundreds of thousands or millions in a large enough app.
Interestingly you can think of it as the counterpoint to the (IIRC) early 2000s Java migration from M:N threading model (where M java threads would map on N OS threads) to 1:1 threading model (where the JVM finally gave up the belief it knew better than the OS and just mapped them 1:1).
On the other hand, there's also lots to be said about async style APIs (eg: goroutines in Go, or the recent async work in Rust; admittedly neither are managed languages) creating lots and lots of very short lived threads where in a managed language context, the JIT would be able to prove interesting properties (eg: it's extremely short-lived, blocks the "parent" thread until it finishes and uses no memory shared with concurrent threads) and treat it in a way that bypasses the whole "save CPU state in the OS" context switch.