How do you ensure thread safety when working with shared resources in a multi-threaded Java application?