简体   繁体   中英

Behavior of synchronized method and block is different

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 using synchronized(target) , and target is a shared instance of Callme . 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 of Callme so their behavior is serial: a thread will hold the monitor of that Callme instance for the whole duration of the Caller.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 to System.out ).

Some additional remarks:

  • Calling Thread.currentThread().join() is a bad idea, because it will wait on itself
  • In general don't create and start Thread instances in the Runnable 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 the Thread , which is not a big problem in this code, but might lead to subtle bugs in more complex applications.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM