user1129812
user1129812

Reputation: 2889

"synchronized(this)" vs. "synchronized((BaseClass)this)" in Java?

This is a successor of my previous question, Is this variable being safely accessed by using synchronization?

For the following program,

Class SubClassB extends SuperClassA {
     protected int c;

     public void inc() {
          synchronized (this) {
               c++;
          }
     }

     public void dec() {
          synchronized ( (SuperClassA) this) {
               c--;
          }
     }
}

Would the counter "c" be accessed thread safe ? I am not sure that in the "dec()" method, is the SuperClassA cast "this" reference a valid object for the synchronized block ? If yes, would the two synchronized blocks lock the same "this" object ? (As it seems to me that "(SuperClassA) this" is not equal to "this")

This strange simulated code comes from the following real life example where SuperClassA is a base class that is not supposed to be modified,

Class SuperClassA {
     protected int c;

     public void dec() {
          synchronized (this) {
               c--;
          }
     }
}

Class SubClassB extends SuperClassA {

     public void inc() {
          synchronized (this) {
               c++;
          }
     }

     public void dec() {
          super.dec();
     }
}

In this example, the "dec()" method in SubClassB calls its superclass's "dec()" method which performs a lock to a "this" object that I suppose to be "SuperClassA.this". If the locked object in SubClassB's "inc()" method is not exactly the same as the locked object in SubClassB's "dec()" method, then I am wondering the inherited counter "c" in SubClassB may NOT be safely accessed by different threads. I feel that there is some ambiguities in using the "this" reference in synchronized blocks.

In the real life example, if I want SubClassB's counter "c" to be thread safe, do I need to add one more synchronized block in its "dec()" method, like this,

Class SubClassB extends SuperClassA {

     public void inc() {
          synchronized (this) {
               c++;
          }
     }

     public void dec() {
          synchronized (this) {
               super.dec();
          }
     }
}

But it seems that such added block is not elegant and may be redundant !

Does anyone have some ideas on these questions. Thanks in advance.

Lawrence

Upvotes: 10

Views: 1581

Answers (4)

Michael Borgwardt
Michael Borgwardt

Reputation: 346526

it seems to me that "(SuperClassA) this" is not equal to "this"

Wrong; synchronization is done on objects, and casting only changes the compile-time type, is has no effect on object identity.

Thus, you don't have to add an extra synchronization in the subclass.

Upvotes: 3

David
David

Reputation: 680

Would the counter "c" be accessed thread safe ?

Yes, it's using the same lock object.

I am not sure that in the "dec()" method, is the SuperClassA cast "this" reference a valid object for the synchronized block ?

Yes.

If yes, would the two synchronized blocks lock the same "this" object ? (As it seems to me that "(SuperClassA) this" is not equal to "this")

Yes. Even if you cast the instance to something it can be casted to (even Object), it'll still refer to the same object.

[...] But it seems that such added block is not elegant and may be redundant !

It is redundant. Extra synchronization is required only if you call multiple synchronized methods and the combined effect must be atomic.

Upvotes: 5

JB Nizet
JB Nizet

Reputation: 692181

The code is thread-safe, because (SomeObject) this adn this are the same object. A cast doesn't transform an object into another object.

The code lacks encapsulation, though, because it lets any subclass access the protected c field in an unsynchronized way. So, any subclass might use c++ or c-- without any synchronization. The field should be private.

Upvotes: 6

NPE
NPE

Reputation: 500893

All three examples are correct as far as synchronization is concerned.

  1. There's only one monitor associated with any object.
  2. Casting this to a base class inside synchronized makes no difference.
  3. For the same object, it doesn't matter whether synchronized(this) is invoked in the context of the derived class or the base class: the same lock is used in both cases.

Upvotes: 2

Related Questions