I have created two sample classes, please ignore the quirks like accessing the Map by get(1L). I recreated the data structures I have in my real project in a very simple way because I am struggling with waking the thread up.
First is the MyThread class. All it does is creating a thread, creating an instance of Handler and passing the Handler the thread and then waiting for the thread via join(). All the thread does is wait. It is synchronized on "this" which I assume is the instance of the thread (anonymous inner class).
public class MyThread {
public static void main(String[] args) {
MyThread t = new MyThread();
}
public MyThread() {
Thread t = new Thread() {
@Override
public void run() {
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("interrupted");
}
}
}
};
Handler handler = new Handler(t);
System.out.println("starting thread");
t.start();
try {
t.join();
System.out.println("joined! (woke up)");
} catch (InterruptedException e) {
System.out.println("MyThread interrupted");
}
}
}
The Handler class has a map. To each key there is a set with threads assigned. After 3 times the Handler is supposed to wake up the thread in MyThread via "t.notify()". I synchronized on the same monitor "t" right? Yet after it notified the thread in MyThread nothing happens. But if I change the t.notify() to t.interrupt() it does interrupt the waiting thread and the thread is joined in MyThread. I have zero clue as to why that is and was hoping for some clarification.
public class Handler {
NavigableMap<Long, Set<Thread>> map = new TreeMap<>();
public Handler(Thread t2) {
System.out.println("in Handler");
map.put(1L, new HashSet<>());
map.get(1L).add(t2);
Thread t = new Thread(new Runnable() {
int counter = 0;
@Override
public void run() {
try {
while (true) {
System.out.println("waiting: " + counter);
Thread.sleep(3000);
if (++counter == 3) {
for (Thread t : map.get(1L)) {
synchronized (t) {
System.out.println("notifying!");
t.notify();
System.out.println("notified!");
}
}
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
}
I have seen multiple questions on here where it was just synchronized on a different monitor but here I am synchronizing on "this" and then the same instance so it should be the same no?
Please excuse all the simplifications (for example the infinity loop) I just wanted to create an easy executable example.
Created the test example. I was expecting that the notify() would wake up the waiting thread.
It works for me. You should remove the race condition. Plus you seem to be using quite a few objects so you very well could be waiting on a different object than you're notifying. If the only change is the interrupt for the wait it should work.
public class Junk{
Thread t;
volatile boolean waiting = false;
public Junk(){
t = new Thread(){
@Override
public void run(){
synchronized(this){
waiting = true;
try{
wait();
}catch(Exception e){
e.printStackTrace();
}
System.out.println("finished");
}
}
};
}
public static void main(String[] args) throws Exception{
Junk y = new Junk();
y.t.start();
while( !y.waiting ){
System.out.print(".");
}
System.out.println("go");
synchronized(y.t){
y.t.notify();
}
y.t.join();
}
}
As pointed out in the comments, notify only awakens one waiting thread if I add another wait on the same object, then notify will not release one of the waiting threads.
Junk y = new Junk();
new Thread( ()->{
synchronized(y.t){ try{ y.t.wait();}catch(Exception e){} System.out.println("waited");}
}).start();
y.t.start();
All of the threads get released by switching notify()
with notifyAll();
This is especially a problem with Thread because it is very public. At the very least you should use a different object that isn't globally used for synchronization.