From: SynchronizationStrategies
Often, because we AvoidThreadsForOptimizations, our threads exist merely as a reflection of the problem environment. However, interactions between threads still require synchronization. In other situations, when we have threads for other reasons, we can still OptimizeLater and ignore the OverHead of synchronization.
Moreover, synchronization is often complex, especially when FineGrainLocking? can quickly lead to deadlocks and when it is difficult to determine exactly what should be locked and for what duration. It can get even worse when thread/component interactions become complicated in their own right making it impossible to correctly weave locks throughout the system. In fact, a ProofOfCorrectness becomes geometrically more difficult with more locks as well.
Therefore, use CoarseGrainLocking. That is, lock large swathes of program code in one big chunk. That way, you can guarantee that only one path of execution at a time will interact with a complex part of the system. Lock entire classes (ala ApartmentThreading? or MonitorLock?(s)). Lock entire methods. Lock entire packages (typically through a FacadePattern).
It will be much easier to understand what's going on in your system if you use CoarseGrainLocking.
But, coarse grain locks are slow. Use them sparingly and when necessary. Also, CoarseGrainLocking also increases the chance of deadlocking if done wrong.
Consider two threads that each acquire a lock each in two distinct superstructures in the system. Each merrily continues on until they hit the "barn door" of the respective other superstructure. Deadlock. The bigger the locks, the more likely this is.this is help ful in monitoring the process See also: CriticalSectionFusing, TrafficCop, SynchronizedTightGroupsOfClasses