Reputation: 105227
Are the following classes equivalent in Java? if no, why?
class Abc {
private int c = 0;
public synchronized void add(int a) {
c += a;
}
public synchronized void subtract(int a) {
c -= a;
}
}
class Def {
private int c = 0;
private final Object lock = new Object();
public void add(int a) {
synchronized(lock) {
c += a;
}
}
public void subtract(int a) {
synchronized(lock) {
c -= a;
}
}
}
Also, what would be the problem of in Def
, using this
as synchronized parameter instead of lock
? Is the following the problem?
Def def = new Def()
synchronized (def) {
def.add(5); //originates deadlock? or is this allowed AS LONG
//as all this happens in the same thread?
}
Are synchronized statements in Java just like C#'s lock statements? If no, what are their differences? If yes, why doesn't C# also allow to lock the methods, like Java allows?
So I guess the problem with synchronized (this)
can be depicted by the following example?
class Xyz {
private int c;
public void add(int a) {
synchronized(this) {
c += a;
}
}
public void subtract(int a) {
synchronized(this) {
c -= a;
}
}
public void show666() {
return 666;
}
}
Thread1 calls xyz.add(0)
and at the same time Thread2 tries to call xyz.show666()
.
Thread2 has to wait for Thread1 to finish with xyz.add(0)
althtough
it doesn't need any info directly related with the lock. Is that it?
Upvotes: 2
Views: 791
Reputation: 87603
1 Are the following classes equivalent in Java? if no, why?
They differ by what instance they synchronize on and thus are affected by the differences between those instances.
Class Abc
uses the Abc
instance that the method is being invoked upon. Other code with access to the instance can use it to synchronize as well by explicitly using the instance in a synchronized block.
The synchronize code blocks in Class Def
explicitly name the instance they synchronizes with. Since this instance is private to the Def
instance, code external to Def
cannot use it to synchronize (unless the instance is somehow leaked).
Some may argue that the Def
approach is much safer, that it is important to encapsulate your locks for the same reason you should use private instance variables.
Finally, there are the differences between the synchronized keyword and using the synchronized statement, e.g. the synchronized statement can lock on any object, not just the instance of the executing code, the synchronize keyword is slightly more efficient, etc.
2a Also, what would be the problem of in Def, using this as synchronized parameter instead of lock?
No problem. Using synchronized keyword on an instance method is semantically identical to wrapping the code of the method in a synchronized block synchronized on this
(the synchronized keyword is slightly more efficient). Using the synchronized keyword on a static method is the same as using a synchronized block synchronized on the class itself (e.g. synchronized(FooBar.class) { ... }
).
2b Is the following the problem?
No, locks in Java are reentrant, which just means that the thread holding the lock on the protecting instance can enter and exit any other code block synchronized on the same instance.
3a Are synchronized statements in Java just like C#'s lock statements? If no, what are their differences?
Semantically equivalent.
But note this answer about Monitor.Enter
and Monitor.Exit
Are there any differences between Java's "synchronized" and C#'s "lock"?
3b If yes, why doesn't C# also allow to lock the methods, like Java allows?
It does - use the [MethodImpl(MethodImplOptions.Synchronized)]
annotation.
4 Thread1 calls xyz.add(0) and at the same time Thread2 tries to call xyz.show666(). Thread2 has to wait for Thread1 to finish with xyz.add(0) althtough it doesn't need any info directly related with the lock. Is that it?
No, a lot of people assume "locking" an instance affects the entire instance. It only affects synchronized code (synchronized methods and code within a synchronized statement) that is synchronized on that instance. Unsynchronized code is unaffected (at least until it hits a synchronized statement or calls a synchronized method).
The unsynchronized method show666()
won't cause the thread to block. Nothing changes if the synchronized(this)
statements were changed into synchronized
methods - again show666
won't block (unless its synchronized too).
Upvotes: 3
Reputation: 116306
They are almost equivalent, with one little difference: since Def
uses an internal private object as lock, noone else can lock on that object from the outside world. In case of the Abc
object, it is possible that someone else locks on the object then calls some of its methods from a different thread, which may result in deadlock. The practical possibility of this is faint (as it clearly requires some mischief or ignorance on another programmer's behalf), but is not zero. Therefore some people prefer the Def
style to be on the safe side, although AFAIK the common idiom is as in Abc
.
Java locks are reentrant, so invoking a lock multiple times from the same thread is OK.
Sorry, I am not competent on C#.
Did you mean synchronized(this)
instead of synchronized(lock)
in the code example? Anyway, since show666
is not synchronized, calls to it do not block, even if another call to a synhronized method blocks on the same object.
Upvotes: 1
Reputation: 28069
No they are different. using synchronized(reference){}
obtains the monitor lock on the given reference (your lock object) and using synchronized
in a method declaration uses this
for the monitor lock. The net result is that an exteral caller can't obtain the same lock as the add
and subtract
methods.
There won't be a deadlock, you can hold any number of monitor locks, plus the Def
object uses a different object to lock on. But even if it was the first class Abc
it won't deadlock. Synchronized locks in java are reetrant. So you can in theory attempt to lock them on the same thread as many times as you like.
It's surprising that C# doesn't allow lock
to be used on methods, but it does seem to be similar if not identical to the java synchronized
keyword when used on a reference. One of the design goals of C# was to be familar enough for Java developers to be able to pick it up with out have there minds completely replaced, so I guess this isn't too surprising.
Upvotes: 3
Reputation: 7790
1.Generally, they are different, since monitors of different objects are used in synchronized modules. BTW, these two classes will behave equivalently, if you will not use monitor of Abc class instances (particularly, if you will not use synchronized(abcObject){} outside abc class, where abcObject is an instance of Abc class)
2.a. The following two methods are equal:
public void add(int a) {
synchronized(this) {
c += a;
}
}
and
public synchronized void add(int a) {
c += a;
}
2.b. I don't see any problem in that code: two monitors (def instance's monitor and def.lock instance's monitor) are always locked in strict order (no circular wait condition)
3.Can't say anything about C#
Upvotes: 1
Reputation: 660
see http://download.oracle.com/javase/tutorial/essential/concurrency/locksync.html
It should help you to answer 1 & 2, in particular it states:
Reentrant Synchronization
Recall that a thread cannot acquire a lock owned by another thread. But a thread can acquire a lock that it already owns. Allowing a thread to acquire the same lock more than once enables reentrant synchronization. This describes a situation where synchronized code, directly or indirectly, invokes a method that also contains synchronized code, and both sets of code use the same lock. Without reentrant synchronization, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block.
From that I understand that unless lock is synchronized by someone else, the two class are pretty much equivalent from the outside.
Should not cause any particular problem thanks to Reentrant synchronization
(3. not familiar with C#)
Upvotes: 1