Whenever a question pops up on SO about Java synchronization, some people are very eager to point out that synchronized(this)
should be avoided. Instead, they claim, a lock on a private reference is to be preferred.
Some of the given reasons are:
- some evil code may steal your lock (very popular this one, also has an "accidentally" variant)
- all synchronized methods within the same class use the exact same lock, which reduces throughput
- you are (unnecessarily) exposing too much information
Other people, including me, argue that synchronized(this)
is an idiom that is used a lot (also in Java libraries), is safe and well understood. It should not be avoided because you have a bug and you don't have a clue of what is going on in your multithreaded program. In other words: if it is applicable, then use it.
I am interested in seeing some real-world examples (no foobar stuff) where avoiding a lock on this
is preferable when synchronized(this)
would also do the job.
Therefore: should you always avoid synchronized(this)
and replace it with a lock on a private reference?
Some further info (updated as answers are given):
- we are talking about instance synchronization
- both implicit (
synchronized
methods) and explicit form ofsynchronized(this)
are considered - if you quote Bloch or other authorities on the subject, don't leave out the parts you don't like (e.g. Effective Java, item on Thread Safety: Typically it is the lock on the instance itself, but there are exceptions.)
- if you need granularity in your locking other than
synchronized(this)
provides, thensynchronized(this)
is not applicable so that's not the issue
I think there is a good explanation on why each of these are vital techniques under your belt in a book called Java Concurrency In Practice by Brian Goetz. He makes one point very clear - you must use the same lock "EVERYWHERE" to protect the state of your object. Synchronised method and synchronising on an object often go hand in hand. E.g. Vector synchronises all its methods. If you have a handle to a vector object and are going to do "put if absent" then merely Vector synchronising its own individual methods isn't going to protect you from corruption of state. You need to synchronise using synchronised (vectorHandle). This will result in the SAME lock being acquired by every thread which has a handle to the vector and will protect overall state of the vector. This is called client side locking. We do know as a matter of fact vector does synchronised (this) / synchronises all its methods and hence synchronising on the object vectorHandle will result in proper synchronisation of vector objects state. Its foolish to believe that you are thread safe just because you are using a thread safe collection. This is precisely the reason ConcurrentHashMap explicitly introduced putIfAbsent method - to make such operations atomic.
In summary
As already said here synchronized block can use user-defined variable as lock object, when synchronized function uses only "this". And of course you can manipulate with areas of your function which should be synchronized and so on.
But everyone says that no difference between synchronized function and block which covers whole function using "this" as lock object. That is not true, difference is in byte code which will be generated in both situations. In case of synchronized block usage should be allocated local variable which holds reference to "this". And as result we will have a little bit larger size of function (not relevant if you have only few number of functions).
More detailed explanation of the difference you can find here: http://www.artima.com/insidejvm/ed2/threadsynchP.html
Also usage of synchronized block is not good due to following point of view:
For more details in this area I would recommend you read this article: http://java.dzone.com/articles/synchronized-considered
Well, firstly it should be pointed out that:
is semantically equivalent to:
which is one reason not to use
synchronized(this)
. You might argue that you can do stuff around thesynchronized(this)
block. The usual reason is to try and avoid having to do the synchronized check at all, which leads to all sorts of concurrency problems, specifically the double checked-locking problem, which just goes to show how difficult it can be to make a relatively simple check threadsafe.A private lock is a defensive mechanism, which is never a bad idea.
Also, as you alluded to, private locks can control granularity. One set of operations on an object might be totally unrelated to another but
synchronized(this)
will mutually exclude access to all of them.synchronized(this)
just really doesn't give you anything.No, you shouldn't always. However, I tend to avoid it when there are multiple concerns on a particular object that only need to be threadsafe in respect to themselves. For example, you might have a mutable data object that has "label" and "parent" fields; these need to be threadsafe, but changing one need not block the other from being written/read. (In practice I would avoid this by declaring the fields volatile and/or using java.util.concurrent's AtomicFoo wrappers).
Synchronization in general is a bit clumsy, as it slaps a big lock down rather than thinking exactly how threads might be allowed to work around each other. Using
synchronized(this)
is even clumsier and anti-social, as it's saying "no-one may change anything on this class while I hold the lock". How often do you actually need to do that?I would much rather have more granular locks; even if you do want to stop everything from changing (perhaps you're serialising the object), you can just acquire all of the locks to achieve the same thing, plus it's more explicit that way. When you use
synchronized(this)
, it's not clear exactly why you're synchronizing, or what the side effects might be. If you usesynchronized(labelMonitor)
, or even betterlabelLock.getWriteLock().lock()
, it's clear what you are doing and what the effects of your critical section are limited to.I'll cover each point separately.
I'm more worried about accidentally. What it amounts to is that this use of
this
is part of your class' exposed interface, and should be documented. Sometimes the ability of other code to use your lock is desired. This is true of things likeCollections.synchronizedMap
(see the javadoc).This is overly simplistic thinking; just getting rid of
synchronized(this)
won't solve the problem. Proper synchronization for throughput will take more thought.This is a variant of #1. Use of
synchronized(this)
is part of your interface. If you don't want/need this exposed, don't do it.While you are using synchronized(this) you are using the class instance as a lock itself. This means that while lock is acquired by thread 1 the thread 2 should wait
Suppose the following code
Method 1 modifying the variable a and method 2 modifying the variable b, the concurrent modification of the same variable by two threads should be avoided and it is. BUT while thread1 modifying a and thread2 modifying b it can be performed without any race condition.
Unfortunately, the above code will not allow this since we are using the same reference for a lock; This means that threads even if they are not in a race condition should wait and obviously the code sacrifices concurrency of the program.
The solution is to use 2 different locks for two different variables.
The above example uses more fine grained locks (2 locks instead one (lockA and lockB for variables a and b respectively) and as a result allows better concurrency, on the other hand it became more complex than the first example ...