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), andtargetis 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 ofCallmeso their behavior is serial: a thread will hold the monitor of thatCallmeinstance for the whole duration of theCaller.runmethod, 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 itselfThreadinstances in theRunnableimplementation 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.