Waiting for Java. Topic does not resume after notification.
I have a program below that has 2 threads T1 and T2. T1 starts first and goes into standby state. T2 triggers a notification. Why doesn't thread T1 resume and print "Thread-0 is awakening"
public class WaitNotifyTest{
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " is running");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is waken up");
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (this) {
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " is running");
notify();
System.out.println(Thread.currentThread().getName() + " notifying");
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}
}
The exit goes like
Thread-0 is running
Thread-1 is running
Thread-1 notifying
Note after this output my program does not end / end. Can someone please explain why my program is not ending.
source to share
There are two problems in your code 1. You have to use the same object for communication between the thread. 2. Wait and report at the same facility where you made the lock.
public class WaitNotifyTest{
public static void main(String[] args) {
Object lock=new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " is running");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is waken up");
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " is running");
lock.notify();
System.out.println(Thread.currentThread().getName() + " notifying");
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}
}
source to share
I think it has something to do with what is being synchronized (this)
used for blocking.
In t1, this will refer to the anonymous class object created there.
In t2, this will refer to an Object of another anonymous class created there. In simple terms, both refer to different objects.
To work wait-notify
you must have a lock on the same object.
source to share
The problem is that you are not syncing the same object. Try printing this
on each of the threads to check. Then, try to synchronize the same object, call wait
and notify
this lock (otherwise you will not get the correct answer).
public class WaitNotifyTest{
public static void main(String[] args) {
Integer someObject = 2;
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t1 this = " + this);
synchronized (someObject) {
System.out.println(Thread.currentThread().getName() + " is running");
try {
someObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is waken up");
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t2 this = " + this);
synchronized (someObject) {
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " is running");
someObject.notify();
System.out.println(Thread.currentThread().getName() + " notifying");
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}
}
source to share
You are not using wait
it notify
correctly.
You have to synchronize the same object so the threads can wait - notify each other. You can share the object among them and cause wait
and notify
with the synchronized block, using the same total Object
as the monitor. Below is the problem:
public static void main(String[] args) {
Object obj = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
System.out.println(Thread.currentThread().getName() + " is running");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is waken up");
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " is running");
obj.notify();
System.out.println(Thread.currentThread().getName() + " notifying");
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}
Note that synchronized (this)
now synchronized (obj)
, and wait wait
and notify
not obj.wait()
and obj.notify()
.
source to share
I agree with the other answers that you sync with the wrong object.
I suggest thinking in monitors. Monitors are objects that restrict concurrent access in order to maintain an internally consistent state. So the sync is explicitly provided by the monitor and is not propagated all over the place in the same thread. Threads do not have to sync with each other, they should be in sync with the resource they are trying to access.
public class Main {
private static class Monitor {
public synchronized void operation1() {
System.out.println(Thread.currentThread().getName() + " is running");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is waken up");
}
public synchronized void operation2() {
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " is running");
notify();
System.out.println(Thread.currentThread().getName() + " notifying");
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
public static void main(String[] args) {
Monitor monitor = new Monitor();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
monitor.operation1();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
monitor.operation2();
}
});
t1.start();
t2.start();
}
}
source to share
In addition to the fact (mentioned by others) that you are blocking different objects, you have another problem in your code. You use Thread.sleep
to make sure the waiting thread starts waiting before other calls notify
, but you still have no indication that it won't happen the other way around, forcing the waiting thread to wait forever.
You should also use some "condition variable" in the while loop to be safe.
Read the oracle docs for examples and further explanations.
source to share