0

I have some code like this:

public class HelloWorld {
    public static void main(String[] args){
        ThreadB b = new ThreadB();
        b.start();

        Runnable a = new Runnable(){
            public void run(){
                System.out.println(Thread.currentThread().getId());
                synchronized(b){
                    try{
                        System.out.println("Waiting for b to complete...");
                        b.wait();
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println("Total is: " + b.total);
                }
            }
        };

        (new Thread(a)).start();

        synchronized(b){
            System.out.println(Thread.currentThread().getId());
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
}

Class ThreadB:

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            System.out.println("Total is: " + total);
            notify();
        }
    }
}

Basically, I have two threads which lock on the Threadb object b. When I run the code, I see:

1
Waiting for b to complete... 
22
Waiting for b to complete...

Here, the numbers are the thread ids, so clearly, they are different threads. Also, the object they are locking on is the same(b). However, both are able to enter the synchronized block and wait on the object.

How's it possible?

Moreover, if I insert 2 other lines in the threadB.run() method:

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            System.out.println("Total is: " + total);
            notify();
        }
    }
}

The 2 threads run to completion:

Total is: 4950
22
1
Waiting for b to complete...
Waiting for b to complete...
Total is: 4950
Total is: 4950

It seems that in the older definition of ThreadB.run(), a notify signal was missed by the waiting threads, so they wait indefinitely. Is it correct?

Also, if a thread exits without calling notify(), the lock gets released intrinsically (equivalent to notifyAll()). Is that right?

Gray
  • 115,027
  • 24
  • 293
  • 354
trans1st0r
  • 2,023
  • 2
  • 17
  • 23
  • 1
    Your code would be a lot easier to read if you'd format it more sensibly. It would also help if you'd make it compile - where is `ThreadB` declared? Please post a [mcve] in future – Jon Skeet Jul 27 '16 at 20:56
  • Don't `synchronized` (`wait` nor `notify`) on `Thread` instances. – Sotirios Delimanolis Jul 27 '16 at 20:59
  • @JonSkeet the code window was too long, so I moved the threadB definition into a different para. I have compiled the code. – trans1st0r Jul 27 '16 at 21:04
  • @SotiriosDelimanolis I added on an example from a blog. Is it an evil practice to do this? – trans1st0r Jul 27 '16 at 21:05
  • Oh, missed that. It would have been a lot clearer if it had been in one place to copy/paste. – Jon Skeet Jul 27 '16 at 21:05
  • 1
    @trans1st0r: Yup, it's definitely a bad idea to wait/notify on Thread objects. They use wait/notify internally. In general, I prefer to do all synchronization/wait/notify on plain `Object` instances which aren't exposed anywhere else. (Or use `java.util.concurrent` classes etc.) – Jon Skeet Jul 27 '16 at 21:07
  • @JonSkeet thanks for the tip. – trans1st0r Jul 27 '16 at 21:07

1 Answers1

5

Because calling Object.wait() releases the lock. From the documentation:

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

  1. It seems that in the older definition of ThreadB.run(), a notify signal was missed by the waiting threads, so they wait indefinitely. Is it correct? They wait() until they are notify()'d (which can happen spuriously) or they are interrupted.

  2. Also, if a thread exits without calling notify(), the lock gets released intrinsically (equivalent to notifyAll()). Is that right? Once a thread is wait()ing, the lock is already released.

Sean Bright
  • 118,630
  • 17
  • 138
  • 146
  • Correct, by the way this is a puzzle and a good question (who down voted me) no way to find – Amit Mahajan Jul 27 '16 at 21:01
  • 2
    I downvoted you, amitmah. You didn't say the same thing at all: you didn't say anything about `wait()` releasing the lock; you didn't link to documentation. You just made a vague statement: "During your output when Thread 1 held the lock you got the first line printed and must be during second line output lock is acquired by second thread." (which just describes the behaviour demonstrated in the question without explaining it) and then said you weren't sure. Sorry, it was not a useful answer. – Andy Turner Jul 27 '16 at 21:04
  • Thanks @Sean Bright. I have one more question: So notify basically tells a waiting thread that you may acquire your released lock, correct? If so, can be acquired (by the waiting thread) only after the thread calling notify gets out of the synchronized block that contains the notify call? – trans1st0r Jul 27 '16 at 21:21
  • Sean Bright , if thats the case, then it would be mean that notify is merely a signal, not a release of the lock. – trans1st0r Jul 27 '16 at 21:22
  • 1
    @trans1st0r, from [the documentation](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#notify()) for `notify()` (see the theme here?): _The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object._ – Sean Bright Jul 27 '16 at 21:22
  • 2
    @trans1st0r: "So notify basically tells a waiting thread that you may acquire your released lock, correct?" No. the notify tells the waiting thread to wake up, but doesn't give that thread any special permission to take the lock. The notified thread doesn't have any special advantage compared to other threads that may be contending for the lock. – Nathan Hughes Jul 27 '16 at 21:44