Synchronization serves two purposes: 1) to ensure safety for updates on shared data (e.g. to avoid races conditions), and 2) to coordinate and order actions taken by threads (e.g. handling threads which communicate intermediate results amongst one another).]
One of the most important aspects of parallel programs is that all their possible interleavings must be correct. One possible way to guarantee this is to simply put one lock in the beginning of each thread; however, it is also clear that we want to use as as few constraints as possible, in order to effectively exploit the available concurrency. Thus, the correct placement of locks is not always trivial. In general, locks provide safety and correctness, while condition variable provide ordering.