Search code examples
javamultithreadingconcurrencyscjpocpjp

Thread concurrency - synchronisation and locks.


import java.util.*;
import java.io.*;
import java.util.regex.*;

class ZiggyTest2 extends Thread{

    String sa;

    public ZiggyTest2(String sa){
        this.sa = sa;
    }

    public void run(){
        synchronized(sa){
            while(!sa.equals("Done")){
                try{
                    sa.wait();
                }catch(InterruptedException is){System.out.println("IE Exception");}
            }
        }

        System.out.println(sa);
    }
}

class Test{
    private static String sa = new String("Not Done");

    public static void main(String[] args){
        Thread t1 = new ZiggyTest2(sa);
        t1.start();

        synchronized(sa){
            sa = new String("Done");
            sa.notify();
        }
    }
}

When i run the above program i get the following exception:

Exception in thread "main" java.lang.IllegalMonitorStateException
        at java.lang.Object.notify(Native Method)
        at Test.main(ZiggyTest2.java:35)

A couple of questions:

  • Why the IllegalMonitorStateException? Because Test.sa is assigned to a new String object, i was expecting the ZiggyTest2 thread to wait indefinately because sa.notify() will be called on a different lock than the one used in ZiggyTest2.

  • In the above example, wait() & notify() are called on the "sa" object. What is the difference in say calling notify() on its own and calling notify()/wait() using an object i.e. sa.wait() and sa.notify()?

  • Does it matter that in the Test class the synchronised block has the lock for the sa object and the sa object is static but in the ZiggyTest2 class, the synchronised block uses the same sa object reference but using a non static reference? Given that one is static and the other is not, will they both still be using the same lock?


Solution

  • When you execute

    sa = new String("Done");
    

    you don't change the content of the String references by sa. You assign a new String instance (a new object) to sa. Strings are immutable. It's impossible to change their value.

    This means that you synchronize on sa (first object : "Not Done"), then assign a new object to sa (second object : "Done"), and call notify on this second object. Since you haven't synchronized on the second object, but on the first one, you get an IllegalMonitorException. Calling notify on an object is only allowed if you own he intrinsic lock on the object. That's why a lock should always be final.

    Calling notify() is equivalent to calling this.notify(). So this.notify() and sa.notify() are just calling notify() on two different objects. The first one will notify a thread waiting on this, and the second one will notify a thread waiting on sa.

    The fact that a variable is static or not does not matter. The lock is associated to the object, not to its reference.