Now what you could do is break objects down into annotated types. Consider immutable vs. mutable in combination with thread-unsafe, thread-compatible, and thread-safe. Immutable data that's not thread-unsafe you can share freely across threads, all is well. L2/L3 caches are happy. Mutable that's thread-safe can similarly be shared at will. Then you can force that thread-compatible objects be wrapped & accessed only from a Mutex or transfered between threads as part of a move operation.
Rust gives you the tools to do all of this, and indeed does some of it, but as part of the steep learning curve of the ownership model.
Python's Little Tin God didn't like it. Mostly because I proposed to freeze the code of the program once the second thread started. That takes away much of the dynamism he insists on.
Might be worth looking at again. The separation of data into immutable, unshared, or synchronized is mainstream now.
There aren't many languages with the concept of ownership or moving at all, and trying to retro-fit that is probably going to be not a good experience for anyone involved.
Rust is largely there, in yet another thing it does well, but if you don't want that something like C++ would be probably a better place to try it. There you at least have move & ownership as a language concept already.