Synchronization is a relatively simple way to eliminate sharing problems, but it must be used very carefully.
The purpose of synchronization is to let tasks run safely in parallel, but it does this by not letting them run in parallel when it would be unsafe. A task that is waiting for a lock is not doing any work at all.
Also, acquiring and releasing locks take a non-trivial amount of time. It is easy to write tasks that spend more time doing synchronization than doing useful work.
Taken together, these two issues mean that you need to be careful how much you use synchronization. Synchronization should be used carefully to solve specific problems. If you find yourself synchronizing large portions of your tasks, you may need to rethink your task structure so that you can get useful tasks that can run safely without so much synchronization.
One strategy is for a task to synchronize its import of the data it needs into private memory locations, work on this private data, and then synchronize the export of the results.
The final problem with synchronization is the danger of deadlocks. A deadlock happens when one or more threads cannot make progress. This can happen, for example, when a task has acquired one lock and is trying to acquire another, while another task has acquired this second lock and is trying to acquire the first. This situation is called deadlock, and it could cause a program to hang forever.
After adding synchronization, to see whether your changes have solved the problems, run the Dependencies tool again. This may reveal previously hidden and newly introduced problems. For example, after you add locks, run the Dependencies tool again to make sure you have not accidentally introduced deadlocks into your program or unbalanced pairs of annotations. The Dependencies tool can detect potential deadlocks because in addition to memory data accesses, it observes the lock events when the annotated program runs.