The trigger point for this post was that a colleague of mine got into a discussion with a speaker at jFokus about what happens when you "synchronize" (the speaker even blogged a bit about it). Now I know that my colleague usually knows what he is talking about, so what is it? Is synchronization expensive or not?
When I asked my colleague he said that grabbing an uncontended lock is on the order of 10-8 seconds. As I had been benchmarking a hash set implementation, I had everything set up to test (and confirm) this. On my machine the overhead for grabbing an uncontended lock (putting the synchronized keyword on a method) was 70ns. Now that sounds pretty cheap, doesn't it? (I have subsequently found out that there are strange issues when running benchmarks. Running again in a supposedly more correct way, the cost of synchrnization would seem to be more like 7ns, or rather, with other noise involved, between 1 and 30, mostly under 10. So my colleague was dead on.)
Well, considering that I'd just been very happy about reducing the execution time of the method from 30ns to 20ns, the prospect of adding a 70ns overhead was not appealing. So synchronization is expensive, then?
Turning it around again, how much does it cost you to get stale data in some threads (the data may actually never be updated for some threads)? How much does it cost you if your object gets into an inconsistent state? In that perspective synchronization is usually very cheap.
The conclusion must be that you have to do what you have to do to protect yourself against the bad effects of unprotected access to shared mutable state and asking whether synchronization is expensive does not necessarily lead you in the right direction. A better question to ask would be "Can I do this in some other way than by accessing shared mutable state?"
Accessing shared mutable state is really the issue to look at (and where my synchronicity is leading me) and it is fraught with more dangers than just avoiding race conditions. Spot the bug (and I don't mean the obvious one that the access to lineNumber isn't protected):
(Background to this example). An example of avoiding access to shared mutable state can be found in my first blog post.
final int lineNumber = new int;
final String line = in.nextLine();
public void run()
statusLine.setText("" + lineNumber);