I observed a scenario where use of synchronized method or synchronized block producing different results. From below code:
class Callme {
void call(String msg) {
System.out.print("[" + msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("]");
}
}
class Caller implements Runnable{
String msg;
Callme target;
Thread t;
public Caller(Callme target, String msg) {
this.target = target;
this.msg = msg;
t = new Thread(this, "Caller thread");
t.start();
}
@Override
public void run() {
synchronized(target) {
target.call(msg);
new Callme().call(msg);
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Callme obj = new Callme();
new Caller(obj, "thread1");
new Caller(obj, "thread2");
new Caller(obj, "thread3");
Thread.currentThread().join();
}
}
When I use the synchronized block in the Caller::run method the ouput is synchronized as below:
[thread1]
[thread1]
[thread3]
[thread3]
[thread2]
[thread2]
But when I use the synchronized method for the Callme::call method, instead of synchronized block, the output is not synchronized:
[thread1]
[thread1[thread2]
]
[thread3[thread2]
]
[thread3]
My Expectation is the output should not be synchronized on both cases because I am using different objects when calling the "Callme::call" method
This made me question my understanding of the Synchronized block concept?
A synchronized method is equivalent to a
synchronized(this)
-block for the length of the entire method, however your code is usingsynchronized(target)
, andtarget
is a shared instance ofCallme
. In other words: the object being synchronized on is different, so the behavior is not the same.In the case you use
synchronized(target)
, it means that all threads synchronize on the same instance ofCallme
so their behavior is serial: a thread will hold the monitor of thatCallme
instance for the whole duration of theCaller.run
method, so in effect the threads are executed one after the other.In the case of a synchronized method, the threads each synchronize on their own instance of
Caller
, so in effect there is no serialization (except for the writes toSystem.out
).Some additional remarks:
Thread.currentThread().join()
is a bad idea, because it will wait on itselfThread
instances in theRunnable
implementation that is going to be run by that thread: you lose access to the thread. Especially don't do this in the constructor, because you are publishing a partially constructed object to theThread
, which is not a big problem in this code, but might lead to subtle bugs in more complex applications.