Static versus non-static lock object in synchronized block
Asked Answered
P

1

44

Trying to visualize and understand synchronization.

  1. What are the differences between using a static lock object (code A) and a non-static lock object (code B) for a synchronized block?
  2. How does it differ in practical applications?
  3. What are the pitfalls one would have that the other wouldn't?
  4. What are the criteria to determine which one to use?

Code A

public class MyClass1 {
  private static final Object lock = new Object();
  public MyClass1() {
    //unsync
    synchronized(lock) {
      //sync
    }
    //unsync
  }
}

Code B

public class MyClass2 {
  private final Object lock = new Object();
  public MyClass2() {
    //unsync
    synchronized(lock) {
      //sync
    }
    //unsync
  }
}

Note

The above code shows constructors, but you could talk about how the behavior is different in a static method and a non-static method too. Also, would it be advantageous to use a static lock when the synchronized block is modifying a static member variable?

I already looked at answers in this question, but it's not clear enough what the different usage scenarios are.

Puma answered 21/8, 2013 at 11:58 Comment(6)
"Note that the above code shows constructors," That is an important distinction. A synchronized block with an instance field on a constructor is absolutely pointless, as no two threads will ever execute a constructor on the same instance.Sokotra
Actually, come to think of it, that instance field could contains an object that is shared among threads. For example private final Object lock = "niceLiteralString";.Sokotra
I understand using constructors to show an example was a bad idea. My question extends to static and non-static methods as well. Also, how does sharing the static/instance field affect the locking?Puma
"how does sharing the static/instance field affect the locking?" The lock is not on the field, but on the object instance that the field contains. So synchronized (a){}; synchronized(b){}; synchronized(c){} could all end up using the same lock. They can also throw NullPointerExceptions, if that field is null.Sokotra
@Sokotra this article makes the argument that String literals should be avoided for a good reason - reuse of references by JVM.Puma
That was just an example to illustrate that even if your field is private, the object contained there may not be.Sokotra
A
60

The difference is simple: if the locked-on object is in a static field, then all instances of MyClass* will share that lock (i.e. no two objects will be able to lock on that object at the same time).

If the field is non-static, then each instance will have its own lock, so only calls of the method on the same object will lock each other.

When you use a static lock object:

  • thread 1 calls o1.foo()
  • thread 2 calls o1.foo(), will have to wait for thread 1 to finish
  • thread 3 calls o2.foo(), will also have to wait for thread 1 (and probably 2) to finish

When you use a non-static lock object:

  • thread 1 calls o1.foo()
  • thread 2 calls o1.foo(), will have to wait for thread 1 to finish
  • thread 3 calls o2.foo(), it can just continue, not minding thread 1 and 2

Which one of those you'll need depends on what kind of data you try to protect with your synchronized block.

As a rule of thumb, you want the lock-object to have the same static-ness than the operated-on value. So if you manipulate non-static values only, you'll want a non-static lock object. If you manipulate static values only, you'll want a static lock object.

When you manipulate static and non-static values, then it'll become complicated. The easy way would be to just use a static lock object, but that might increase the size of the synchronized-block more than absolutely necessary and might need to more lock contention than desired. In those cases you might need a combination of static and non-static lock objects.

In your particular case you use the lock in the constructor, which will only ever be executed once per instance, so a non-static lock-object doesn't make any sense here.

Airs answered 21/8, 2013 at 12:4 Comment(6)
I think using the constructor as an example was a bad idea. Anyway, good answer. I want to ask one more question. Is there a way of ensure that a set of methods operating on a volatile static member variable via one thread will be done and over with before other threads access this member variable? I can make this a separate question on SO if necessary.Puma
@ADTC: that does sound like a separate question to me. Short answer: no, you'll need synchronization. You can avoid it only if the operation of the volatile is atomic (for example a single ++ on an int**and** you don't care about the order (i.e. you don't care if you read before or after the operation).Airs
I do care about the read, it should only happen after the operation. I think I will ask it as a separate question.Puma
@ADTC: actually, I mis-informed you: even x++ isn't atomic on a volatile x. Do go ahead and ask a new question for this.Airs
if two threads are accessing same object's two diff non-static methods , it is possible or the second thread has to wait , does the lock on one object locks all non-static methods ??Parnas
@HussainAkhtarWahid'Ghouri' Yes, each thread(accessing same object resources) need to wait until other thread leave lock on any same-instance-synchronized-method.Jerri

© 2022 - 2024 — McMap. All rights reserved.