Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does it matter what object I use wait()/notify() on, if I just want a way to signal between threads?

So I have a classic case of "my code works, but I dont know why".

I'm have a program that creates a thread, and when I receive a certain input from scanner, I pass control of the string to a worker thread. To do this, I make my thread wait(), and when I get the correct input from my UI thread, I notify().

Here is my code. For simplicity, I've just used one thread.

package main;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;


class ThreadDemo extends Thread {
       private Thread t;
       private String threadName;
       volatile Boolean keepRunning = true;
       private Queue<String> q = new LinkedList<String>();


       ThreadDemo( String name){
           threadName = name;
           System.out.println("Creating " +  threadName );
       }

       public void in(String ex){
           q.add(ex);
           System.out.println("Added " + ex + "to queue of " + threadName);
           synchronized(t){
               t.notify();
           }       
       }


       public void run() {
           System.out.println("Starting to loop.");
            while (keepRunning) {
                try {
                    //Why does it matter that I synchronized t? 
                    synchronized(t){
                        System.out.println(threadName +  "Waiting");
                        t.wait();
                    }
                } catch (InterruptedException e) {
                    System.out.println("Thread interrupted " + e.toString());
                    }
                System.out.println(threadName +  "notified");
                if (q.size()>0){
                    String out = q.remove();
                    System.out.println(threadName + "received " + out);
                }
            }
            System.out.println("Done looping.");
       }

       public void start ()
       {
          System.out.println("Starting " +  threadName );
          if (t == null)
          {
             t = new Thread (this, threadName);
             t.start ();
          }
       }
    }


public class DataAnalysisProgram {

    public static void main(String[] args) {
          ThreadDemo T1 = new ThreadDemo( "Thread-1");
          T1.start();

          System.out.println("say something");
          Scanner s = new Scanner(System.in);
          String t;
          do{
              t = s.next();
              T1.in(t);
          }while (!t.equals("stop"));

          T1.keepRunning = false;
          T1.interrupt();
          s.close();
    }
} 

So this works fine. My thread waits until I use notify. However, I dont really understand the significance of which object I call notify and wait on.

In my implementation, I willy nilly did t.wait()/t.notify(), where t is my thread object. I suppose it would still work if I did threadName.wait()/threadName.notify(). Why do we call notify and wait on seemingly arbitrary objects? I know I'm missing a concept here about notify and wait.

like image 802
Eric S. Avatar asked Feb 11 '23 21:02

Eric S.


2 Answers

In fact, you broke the contract when you invoked wait on a Thread instance:

It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

This is because Thread uses that for its own internal purposes.

To answer your question: the Thread object is not a thread. Saying t.notify() does not notify t, it notifies a thread waiting on t's monitor. In this context an instance of Thread is just another Java object, and all Java objects have a monitor associated with them.

Your suggestion to use the monitor of String threadName is another bad idea because you don't control the lifecycle of string instances and may easily trample upon issues with interned strings.

It is recommended practice not to involve arbitrary object's monitors in thread coordination, but prefer to use dedicated instances of Object for that. This is motivated by the general advantages of the principle of the separation of concerns.

like image 169
Marko Topolnik Avatar answered Feb 14 '23 14:02

Marko Topolnik


Why do we call notify and wait on seemingly arbitrary objects?

Calling wait and notify have no direct effect on the object itself, so it is indeed arbitrary. All you need is some object which you can use as a point of communication.

In fact it has been argued as a historical mistake in the Java language that it is possible to use wait and notify on arbitrary objects, because managing the lock adds a small cost and complexity to all objects even though for the overwhelming majority of objects the mechanism is unused. Instead they should have used a dedicated Lock class, or had objects implement a particular interface to indicate you could call wait/notify on them, or something similar.

Anyway, it is a good idea not to use arbitrary objects, because (a) it's confusing, and (b) it risks getting tangled up with other code that might be trying to use the same object. If there is no sensible object available, you should create one for that purpose:

    private final Object lock = new Object();
like image 30
Boann Avatar answered Feb 14 '23 12:02

Boann