The Source for Java Technology Collaboration


Home | Help | Changes | Index | Search | Go

SynchronizationIsSlowMisconception

Introduction

It has been repeated that synchronization is slow many times to the point that many people will go to great lengths to avoid synchronization without a full understanding of what they are avoiding. Synchronization is not free, there is a cost associated with it, but modern JVMs make it very cheap. What is expensive is contention.

What is Contention

Contention happens when two threads compete for one resource, in Java that resource is an instance of an Object. When one thread has acquired a lock on an object and is still holding on to it, contention happens when another thread also tries to acquire a lock on the same object. The second thread is blocked until the first thread has released it's lock. This allows the second thread to acquire a lock and move forward.

What is the cost of Synchronization without Contention

No matter what, when a Thread enters a synchronized block it must test for and acquire a lock. In newer JVMs (1.4.0) this cost is very small. So small that the slight implementation differences in java.util.Vector.set(int,Object) and java.util.ArrayList.set(int,Object) make the Vector faster in the 1.4.0-sever mode despite the fact that Vector's set method is synchronized and ArrayList's set method is not. See Java Performance Tuning page 291 for more info.

What is the cost of Synchronization with Contention

The overwhelming cost of contention is the time spent idle waiting to acquire a lock. During this time a thread cannot move forward and continue to do work. This means that the cost of synchronization with contention includes the work done by any threads that acquire the lock on an object before this thread can continue. For any non-trivial application, the amount of work done in normal program flow completely dwarfs the work needed to acquire synchronization locks by orders of magnitude?.

The Solutions

You cannot avoid synchronization when it's needed. If the algorithm you've chosen to implement requires synchronization for correct behavior, then you must use synchronization; otherwise you possibly allow for very subtle bugs. You can change your algorithm to one that does not require synchronization but this should be avoided unless it is understood that the possible algorithmic optimizations you are giving up are not significant. I personally consider blind avoidance of synchronization to be the same evil as premature optimization.

Small Synchronized Blocks

One very common synchronization advice is to make your synchronized blocks as small as possible. In other words, you are doing as little work as possible inside of a synchronized block of code. The rationale is that since the cost of contention is relatively so expensive, don't worry about other synchronization costs. Also, this method does not require a full understanding of your application's behavior. The downside to this method is that the repeated acquisition and release of synchronization locks may be more expensive than contention if contention is extremely rare or not possible, like a single threaded application.

Immutable Objects

An immutable object cannot change value once it's been created. The [[String java.lang.String] is the most common example of an immutable object. Since an immutable object cannot be modified it is safe to share these objects between threads without the use of synchronization. Depending on their use immutable objects have other performance implications with respect to the garbage collector? that you should be aware of.

ThreadLocal

The java.lang.ThreadLocal class provides a convenient way for an instance to have per-Thread data. This allows you to avoid the need for synchronization because data is not shared between threads, each thread has it's own set of data. I've found this to be very helpful for caching results, let me give an example.

I once wrote a Java Servlet? that transformed XML into HTML. The result of the first few steps were exact same every time but the result of those steps was not thread safe. So, I used ThreadLocal so that each thread could have it's own set of cached data and didn't have to synchronize access to the Servlet. The benefit of not having to repeat a lot of work plus the ability to take advantage of all CPU?s gave a good performance boost at the cost of increased memory consumption.

Understanding your Application

The suggestion to create many small Synchronized blocks, instead of one large one is commonly made because larger sections of synchronized code mean more complexity and the waiting time for other threads can grow quite fast. Opposite to those negative points for big sections lies the point that it still takes more than a little amount of time to acquire the synchonization locks, so more locks means longer waiting time. Making a number of private methods synchronized instead of only the method that calls them means that it always takes n-times the lock time.
The only way to choose the size of your synchonized blocks correctly is to understand your application.

For example; If a cache uses a Hashtable? repeatedly to check, update or delete cache entries the code gets slowed down because a Hashtable? is synchronized on most of its methods. Imagine a method on this cache where you first check the entry exists, then creates one using a put and at the end fetches it again to return to the user.

public Object fetch(String key) {
    if(! myHashtable.containsKey(key)) {
        myHashtable.put(key, createNewEntry(key));
   }
    return myHashtable.get(key);
}

This would be faster if you would use a HashMap? (which is not synchonized) and synchonize your whole method:

public synchonized Object fetch(String key) {
    if(! myHashMap.containsKey(key)) {
        myHashMap.put(key, createNewEntry(key));
   }
    return myHashMap.get(key);
}

Summary

The cost of synchronization is very small. The cost of contention can be large. If you compare the source of Vector.set(...) and ArrayList.set(...) you'll see that the overhead of synchronization is less that the cost of a private method call and an integer comparison in a boolean expression. Pick a faster algorithm and don't get hung up on if it needs to be synchronized for correctness. Do consider the implications of contention when picking an algorithm.

References

-- SandyMcArthur - 16 Jun 2003



Discussion about SynchronizationIsSlowMisconception

Topic SynchronizationIsSlowMisconception . { Edit | Ref-By | Printable | Diffs r7 < r6 < r5 < r4 < r3 | More }
 XML java.net RSS

Revision r7 - 27 Aug 2003 - 03:37:05 - Main.redwolf
Parents: Misconceptions