Because Java did not have a good async model for decades, most Java apps are thread-intensive (thread-per-client) and there's an enormous amount of code that can't easily be refactored into async. Light-weight threads help save all that thread-per-client code from the ash pile of history.
But in general it's best to write async code from the get-go because that forces the programmer to compress application state rather than inefficiently smear parts of it on the stack. Better state compression (because smaller stacks) -> smaller memory footprint per-client (or whatever) -> faster (because of higher cache hit ratios because of less cache thrashing because fewer memory accesses).
We are made of meat. It’s rarely worthwhile to sacrifice developer effort to conserve hardware, and if you do manage to break even today, you probably won’t in the future. Async Java was pretty painful and only penciled out because the cost of a million native threads was just ludicrously higher than everything else.
But in general it's best to write async code from the get-go because that forces the programmer to compress application state rather than inefficiently smear parts of it on the stack. Better state compression (because smaller stacks) -> smaller memory footprint per-client (or whatever) -> faster (because of higher cache hit ratios because of less cache thrashing because fewer memory accesses).