As I understand it Rust's unsafe is designed so you can encapsulate unsafety behind a safe interface.
It's up to the programmer to verify the safety of code using unsafe. But it they do so correctly then you can rely on the borrow checker to verify that everything else is safe.
This means that when you run into a thread safety bug in Rust code you should only have to look at the unsafe blocks to find the culprit.
With that said, I think it is important to mention that if you go off the safe path in rust and you do hit such a bug, your current execution is no longer trustable, it could have corrupted the heap, introduce UB, and die with a segfault, etc.
In java, race conditions can enter illegal application state, but their scope is much more limited. NPEs are 100% safely handled, etc. you can only get off the safe road with using Unsafe unsafe, and manipulating the JVM’s inner state which is not even allowed by default. Depending on application, this difference may matter a lot!