views:

72

answers:

5

Hi Guys, I will try to explain the quetion by taking following 3 cases. CASE I: I was using shared lock for synchronization using something like this:


         private static final String SHARED_LOCK = "shared_lock";
         private static int i = 0;
         private static int j = 0;
    void increment() {
        synchronized (SHARED_LOCK) {
            i++;
            j++;
        }
    }

And it is working fine.

CASE II: Now What I have changed here is instead of using shared lock I thought of using a local lock by doing something like this:


         private static int i = 0;
         private static int j = 0;
    void increment() {
        final String LOCAL_LOCK = "local_lock";
                  synchronized (LOCAL_LOCK) {
            i++;
            j++;
        }
    }

And I found that code was still working fine that is synchronization was still working.

CASE III: However when I changed the local locl to this:

final String LOCAL_LOCK = new String("local_lock");

then the synchronization went away. So it looks like that in CASE II the local lock was able to provide synchronization because of interning of String literals that Java does for us automatically but in CASE III as I was explicitly creating a new String everytime thus synchronization was not happening.

So coming back to my original question. Does anyone feels that CASE II is not the right way to achieve synchronization? If yes could you please also mention why?

Thanks in advance, SacTiw.

+7  A: 

Locks are meant to provide synchronization between code running in separate threads. If you use local locks, each thread will have its own copy of the lock object (in the thread's stack) on which it will lock and hence there will be no synchronization.

It works in your case only because of String interning. If you used final Object LOCAL_LOCK = new Object(); it will not work.

So locks should be always shared between threads.

abhin4v
+1 for interning. One would expect each call to `increment()` to have it's own lock, since it is a local variable. Using `String` is the specialty in this case (causing the synchronization to work unexpectedly), as the VM uses the same `String` instance for all the variables with the same text in it. See http://en.wikipedia.org/wiki/String_interning for details.
f1sh
@abhin4V: I know why case-II works fine. I have already mentioned about automatic interning of String done by Java in my question itself. Also I know why case-III is failing because each thread will have its own version of lock so they wait for othet thread to complete. So what I was really interested in knowing was whether using case-II for synchronization is really safe or are there some hidden issues with case II that may arise latter.
sactiw
@sactiw: If you are always going to use interned String as a lock then it might be ok. But it is still a bad practice because one day someone may change String to any other type and everything will fail mysteriously.
abhin4v
+1  A: 

Its because of string interning. All Strings created as in case 2 with equal values will 'share' the same instance. Strings created as in case 3 will be distinct instances.

It is this difference that accounts for the different synchronisation behaviour you're seeing.

Visage
+3  A: 

Synchronization only works correct if you synchronize on the same object between threads;

  • The 1st case is obvious,
  • In the second case the Java compiler interns the string constant which is why it works too (you get the same object from the string pool between calls),
  • The 3rd case you force a new string object and therefore you are getting your own private lock per thread giving no synchronization at all.
rsp
A: 

This is not an answer but a question to the above answers. Will for the third case if we use shared_locak instead of local,the synchronization work?

Krishanu
Yes. And it is a pretty standard way to do synchonization: `private Object lock = new Object();`
abhin4v
+1  A: 

What's very very important here is that using 'canonicalizable' objects -- like, say, Strings, which can be interned, or Integer constants which may be shared -- is basically opening you up to using the same lock object as another class.

This is, as you'd imagine, a very bad idea. By using the same lock as another class, you can basically guarantee yourself a whole world of painful debugging later as you try and work out why your app deadlocks.

If you need a private lock in a class, make sure it is a completely unique object by obtaining it only via the new operator.

(I believe one of the Java Puzzlers presentations might have covered this; some other example references are here and here)

Cowan
So here you mean that case-2 may lead to a situation where some other threads accessing some other class may start using same lock as this class which will lead to poor performance and painful debugging. Right?
sactiw
In Java Language Specification it is mentioned that in Java literal strings (immutable) within different classes in different packages likewise represent references to the same String object. And this rule applies to all primitive objects as they all are immutable as well.So that means if we go by case-2 that may lead to poor performance and painful debugging in case of facing deadlocks.So I think we can say that synchronization is possible via case-2 in my question but it is not at all recommended way to use.
sactiw
Absolutely, sactiw. It is usually a very bad idea to use anything other than a privately-held, inaccessible-to-other-classes Object for a lock. If you get an object via `new`, you know it's yours and yours alone (as long as you don't leak it out).
Cowan