Good article for basic understanding of thread safety and static utility methods. I think the article does a good job with data sharing examples.
http://http://odetocode.com/Articles/313.aspx
I would only add that it is not only important to watch out for data sharing between threads, but also access and sharing of I/O resources like db connections, files and sockets.
One way would to use lock(obj){ ... } mechanism, where the system runtime will manage its own queue (serialized access), or another way would be to implement your own access queue mechanism.
Implementing your own queue mechanism can give you advanced features, should you need to implement them, such as priority scheduling and better micro-level locking.
By micro-level locking I mean the following. Let's say you have a function named 'SaveFile(...)' which uses lock(obj){...} mechanism to serialize access to a particulat file resource, it is possible to implement this in such a way that a unique lock object is used for each unique file name.
Better, queue can be scanned/sorted by resource objects being accessed and allow concurrent queue processing as long as each work item in the queue processed is accessing a different resource.
In essence, this turns one queue into a multi-queue mechanism where each shared resource has it's own queue. This greatly improves performance and would be similar to record-level locking mechanisms in databases, but instead could be applied to sockets, files, etc...
Lastly, in such multi-queue system priority scheduling can be implemented where queue management code can select by weight or some other calculated heuristic how much time should be dedicated to each queue.