One of the most important requirements implied by "thread safety" is to ensure that the action of one thread, operating on some shared data, can never cause other threads to see the same data in an inconsistent or invalid state.
But, what does "inconsistent or invalid" mean? Only the application developer can say for sure. Often it has to do with relationships between different properties of a single object and/or relationships between different objects.
@Andrew Madsen suggested that making individual properties "atomic" might not be enough. Here's why not:
Suppose that thread A must change three different properties, p1, p2, and p3, in order to do its job; and suppose that thread B needs to look at those same properties. Making an individual property "atomic" ensures that thread B is guaranteed to see either the "before" value or the "after" value when it looks at any given property. It will never see any other value.
But that may not be enough to ensure thread safety. What happens if thread B sees the "before" value of p1, but the "after" values of p2 and p3? In some programs, that could cause thread B to compute a wrong result, store bad data, or even crash the program.
What needs to be "atomic" is the entire operation that thread A performs. We need to ensure that thread B either sees the "before" values of all three properties, or the "after" values of all three, and never anything else.
We implement that guarantee by ensuring that thread A never does its job except when it has a certain mutex locked, and we must also ensure that thread B never looks at those properties unless it has locked the same mutex.