How do you ensure thread safety in a highly concurrent Java application? Explain the difference between synchronized blocks and lock objects.