Why is this simple Java synchronized code block example giving me different output, depending on which object I lock on?

228 views Asked by At

Ok, so I am trying to learn multi-threading. I am reading a book and I came across this example for synchronized code blocks:

class CallMe {
    void call(String msg) {
        System.out.print("[" + msg);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("Interrupted");
        }
        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 );
        t.start();
    }

    public void run() { 
        synchronized (target) {
            target.call(msg);
        }
    }
}

public class Scrap {
    public static void main(String[] args) {
        CallMe target = new CallMe();
        Caller ob1 = new Caller( target, "Hello");
        Caller ob2 = new Caller( target, "Synchronized" );
        Caller ob3 = new Caller( target, "World");

        try {
            ob1.t.join();
            ob2.t.join();
            ob3.t.join();
        } catch (Exception e) {
            System.out.println("Interrupted");
        }
    }
}

I got the following output from this:

[Hello]
[World]
[Synchronized]

Then, since I have done some tutorials online, I know that it is good style (or was told this) to create an instance of Object just for locking. So, I did this and the Caller class became:

class Caller implements Runnable {
    String msg;
    CallMe target;
    Thread t;

    private Object lock = new Object();

    public Caller(CallMe target, String msg) {
        this.target = target;
        this.msg = msg;
        t = new Thread( this );
        t.start();
    }

    public void run() {
        synchronized (lock) {
            target.call(msg);
        }
    }
}

I was a little surprised when I got this as output:

[Synchronized[Hello[World]
]
]

Of course, this is output where interleaving has taken place and it is not correct. My question is why did this happen? I thought that making the lock Object would give me the same results. Why did it (or why would it) not give me the same output? I thought that creating the Object instance was good style and would work the same in this case, and to be honest I can't see why it would be different locking on "target" versus "lock". I guess what I mean to ask is, why does locking on one certain item in this case cause the program to be correct and locking on the other makes it wrong?

1

There are 1 answers

8
RymplEffect On BEST ANSWER

Each instance of Caller locks on a different instance of lock. I think you have to synchronize on shared instances. Making the lock into a static member would share the same instance across all threads.