I have just written a simple java example to get familiar with the concept of wait and notify methods.
The idea is that when calling notify()
, the main thread will print the sum.
MyThread class
public class MyThread extends Thread {
public int times = 0;
@Override
public void run() {
synchronized (this) {
try {
for (int i = 0; i < 10; i++) {
times += 1;
Thread.sleep(500);
if (i == 5) {
this.notify();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Main Class
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
synchronized (t) {
t.start();
try {
t.wait();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(t.times);
}
}
}
Expected Results
5 but I got 10 instead.
Well, what I though is that when notify()
is called, the main thread will wakeup and execute the System.out.println(t.times)
which should give 5. Then the run()
will continue till it finishes the for loop which will update the value of times to 10.
Any help is highly appreciated.
Synchronized blocks imply mutual exclusion. At any given moment, only one thread is allowed to hold the lock and execute the code within a synchronized block. This rule spreads over all the blocks guarded by the same lock.
In your case, there're two such blocks that use the same lock, so it's either the main thread or the MyThread
that is allowed to execute code in either of these blocks, the other thread must wait. So, you have the following scenario here:
wait()
. This call releases the lock and puts the main thread into the WAITING
state.notify()
. This call doesn't release the lock, it just notifies the main thread that it can progress as soon as it can reacquire the lock.times
to 10 and eventually leaves the synchronized block, releasing the lock.println
. But by this time, the times
is already 10.Using join()
won't help you either because the result will be the same – the main thread can only make progress when the second one is finished.
If you want your main thread to continue execution as soon as the second thread hits 5
, you need to acquire the lock and release it immediately after that event:
public class MyThread extends Thread {
public volatile int times = 0;
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
times += 1;
Thread.sleep(500);
if (i == 5) {
synchronized(this) {
this.notify();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
Don't forget to make times
volatile, otherwise JVM won't guarantee that you'll see its actual value in your main thread.
And you should also understand that this approach doesn't guarantee that your main thread prints 5. It might occur that by the time it reaches the println
call, the second thread makes one or two or even more iterations and you'll see something greater than 5 (though it's highly unluckily due to the sleep()
call on every iteration).