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.
In fact, you broke the contract when you invoked wait
on a Thread
instance:
It is recommended that applications not use
wait
,notify
, ornotifyAll
onThread
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.
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With