Application programmers shouldn't have to deal with thread synchronization. This means that the approach to thread safety should be based on avoiding shared state. But application programmers shouldn't have to worry about explicitly avoiding shared state either. The default behavior of the language should just work 90% of the time.
In my language Luan I previously made everything thread-local and incrementally cloned data as needed in new threads. But this was very expensive. I just finished implementing my new approach (not yet committed) which is both safe and efficient (5X speed improvement). What I do now is that all objects start mutable. But as soon as an object is reachable by multiple threads, it becomes immutable. And this works 90% of the time. There are a few cases where the thread-local approach is needed, and this can be done explicitly. Since Luan tables support meta-methods, I can make specific keys thread-local while the rest of the table is immutable. So for example "Io.stdout" is thread-local while most of "Io" is immutable. Most uses of thread-local will be in libraries like "Io", so application programmers really never even need to be aware of this concept.
I think my new approach is the best solution to thread-safety. If you disagree, give me your argument.